Merge branch 'topic/cec' into patchwork
* topic/cec: [media] DocBook/media: add CEC documentation [media] s5p_cec: get rid of an unused var [media] move s5p-cec to staging [media] vivid: add CEC emulation [media] cec: s5p-cec: Add s5p-cec driver [media] cec: adv7511: add cec support [media] cec: adv7842: add cec support [media] cec: adv7604: add cec support [media] cec: add compat32 ioctl support [media] cec/TODO: add TODO file so we know why this is still in staging [media] cec: add HDMI CEC framework (api) [media] cec: add HDMI CEC framework (adapter) [media] cec: add HDMI CEC framework (core) [media] cec-funcs.h: static inlines to pack/unpack CEC messages [media] cec.h: add cec header [media] cec-edid: add module for EDID CEC helper functions [media] cec.txt: add CEC framework documentation [media] rc: Add HDMI CEC protocol handling
This commit is contained in:
@@ -209,6 +209,7 @@ config VIDEO_ADV7604
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7604 video decoder.
|
||||
|
||||
@@ -218,10 +219,18 @@ config VIDEO_ADV7604
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7604.
|
||||
|
||||
config VIDEO_ADV7604_CEC
|
||||
bool "Enable Analog Devices ADV7604 CEC support"
|
||||
depends on VIDEO_ADV7604 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7604 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_ADV7842
|
||||
tristate "Analog Devices ADV7842 decoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7842 video decoder.
|
||||
|
||||
@@ -231,6 +240,13 @@ config VIDEO_ADV7842
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7842.
|
||||
|
||||
config VIDEO_ADV7842_CEC
|
||||
bool "Enable Analog Devices ADV7842 CEC support"
|
||||
depends on VIDEO_ADV7842 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7842 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_BT819
|
||||
tristate "BT819A VideoStream decoder"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
@@ -447,6 +463,7 @@ config VIDEO_ADV7511
|
||||
tristate "Analog Devices ADV7511 encoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7511 video encoder.
|
||||
|
||||
@@ -455,6 +472,13 @@ config VIDEO_ADV7511
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7511.
|
||||
|
||||
config VIDEO_ADV7511_CEC
|
||||
bool "Enable Analog Devices ADV7511 CEC support"
|
||||
depends on VIDEO_ADV7511 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7511 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_AD9389B
|
||||
tristate "Analog Devices AD9389B encoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-dv-timings.h>
|
||||
#include <media/i2c/adv7511.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
@@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2");
|
||||
#define ADV7511_MIN_PIXELCLOCK 20000000
|
||||
#define ADV7511_MAX_PIXELCLOCK 225000000
|
||||
|
||||
#define ADV7511_MAX_ADDRS (3)
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
@@ -90,12 +93,20 @@ struct adv7511_state {
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
int chip_revision;
|
||||
u8 i2c_edid_addr;
|
||||
u8 i2c_cec_addr;
|
||||
u8 i2c_pktmem_addr;
|
||||
u8 i2c_cec_addr;
|
||||
|
||||
struct i2c_client *i2c_cec;
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7511_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
|
||||
/* Is the adv7511 powered on? */
|
||||
bool power_on;
|
||||
/* Did we receive hotplug and rx-sense signals? */
|
||||
bool have_monitor;
|
||||
bool enabled_irq;
|
||||
/* timings from s_dv_timings */
|
||||
struct v4l2_dv_timings dv_timings;
|
||||
u32 fmt_code;
|
||||
@@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
|
||||
static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int i;
|
||||
@@ -242,6 +253,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
|
||||
v4l2_err(sd, "%s: i2c read error\n", __func__);
|
||||
}
|
||||
|
||||
static inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
return i2c_smbus_read_byte_data(state->i2c_cec, reg);
|
||||
}
|
||||
|
||||
static int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
v4l2_err(sd, "%s: I2C Write Problem\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val);
|
||||
}
|
||||
|
||||
static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
@@ -425,16 +464,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = {
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
static void adv7511_inv_register(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
v4l2_info(sd, "0x000-0x0ff: Main Map\n");
|
||||
if (state->i2c_cec)
|
||||
v4l2_info(sd, "0x100-0x1ff: CEC Map\n");
|
||||
}
|
||||
|
||||
static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
reg->size = 1;
|
||||
switch (reg->reg >> 8) {
|
||||
case 0:
|
||||
reg->val = adv7511_rd(sd, reg->reg & 0xff);
|
||||
break;
|
||||
case 1:
|
||||
if (state->i2c_cec) {
|
||||
reg->val = adv7511_cec_read(sd, reg->reg & 0xff);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
|
||||
adv7511_inv_register(sd);
|
||||
@@ -445,10 +496,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
|
||||
|
||||
static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
switch (reg->reg >> 8) {
|
||||
case 0:
|
||||
adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff);
|
||||
break;
|
||||
case 1:
|
||||
if (state->i2c_cec) {
|
||||
adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
|
||||
adv7511_inv_register(sd);
|
||||
@@ -536,6 +595,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct adv7511_state_edid *edid = &state->edid;
|
||||
int i;
|
||||
|
||||
static const char * const states[] = {
|
||||
"in reset",
|
||||
@@ -605,7 +665,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
|
||||
else
|
||||
v4l2_info(sd, "no timings set\n");
|
||||
v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr);
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return 0;
|
||||
|
||||
v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr);
|
||||
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr);
|
||||
return 0;
|
||||
}
|
||||
@@ -663,15 +739,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
|
||||
return true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return -EIO;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
/* power up cec section */
|
||||
adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01);
|
||||
/* legacy mode and clear all rx buffers */
|
||||
adv7511_cec_write(sd, 0x4a, 0x07);
|
||||
adv7511_cec_write(sd, 0x4a, 0);
|
||||
adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready 1 */
|
||||
if (state->enabled_irq)
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39);
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
if (state->enabled_irq)
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00);
|
||||
/* power down cec section */
|
||||
adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7511_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7511_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10);
|
||||
/* set address for mask 0 */
|
||||
adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20);
|
||||
/* set address for mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40);
|
||||
/* set address for mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
/* blocking, clear cec tx irq status */
|
||||
adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38);
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
adv7511_cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
adv7511_cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
adv7511_cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x10) {
|
||||
v4l2_dbg(1, debug, sd,
|
||||
"%s: tx raw: arbitration lost\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x08) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x20) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7511_cec_adap_ops = {
|
||||
.adap_enable = adv7511_cec_adap_enable,
|
||||
.adap_log_addr = adv7511_cec_adap_log_addr,
|
||||
.adap_transmit = adv7511_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Enable interrupts */
|
||||
static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT;
|
||||
u8 irqs_rd;
|
||||
int retries = 100;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable");
|
||||
|
||||
if (state->enabled_irq == enable)
|
||||
return;
|
||||
state->enabled_irq = enable;
|
||||
|
||||
/* The datasheet says that the EDID ready interrupt should be
|
||||
disabled if there is no hotplug. */
|
||||
if (!enable)
|
||||
@@ -679,6 +937,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
||||
else if (adv7511_have_hotplug(sd))
|
||||
irqs |= MASK_ADV7511_EDID_RDY_INT;
|
||||
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0,
|
||||
(state->cec_enabled_adap && enable) ? 0x39 : 0x00);
|
||||
|
||||
/*
|
||||
* This i2c write can fail (approx. 1 in 1000 writes). But it
|
||||
* is essential that this register is correct, so retry it
|
||||
@@ -701,20 +962,53 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
||||
static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
u8 irq_status;
|
||||
u8 cec_irq;
|
||||
|
||||
/* disable interrupts to prevent a race condition */
|
||||
adv7511_set_isr(sd, false);
|
||||
irq_status = adv7511_rd(sd, 0x96);
|
||||
cec_irq = adv7511_rd(sd, 0x97);
|
||||
/* clear detected interrupts */
|
||||
adv7511_wr(sd, 0x96, irq_status);
|
||||
adv7511_wr(sd, 0x97, cec_irq);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status);
|
||||
v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__,
|
||||
irq_status, cec_irq);
|
||||
|
||||
if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT))
|
||||
adv7511_check_monitor_present_status(sd);
|
||||
if (irq_status & MASK_ADV7511_EDID_RDY_INT)
|
||||
adv7511_check_edid_status(sd);
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
if (cec_irq & 0x38)
|
||||
adv_cec_tx_raw_status(sd, cec_irq);
|
||||
|
||||
if (cec_irq & 1) {
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = adv7511_cec_read(sd, 0x25) & 0x1f;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__,
|
||||
msg.len);
|
||||
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = adv7511_cec_read(sd, i + 0x15);
|
||||
|
||||
adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */
|
||||
adv7511_cec_write(sd, 0x4a, 0);
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* enable interrupts */
|
||||
adv7511_set_isr(sd, true);
|
||||
|
||||
@@ -1183,6 +1477,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
|
||||
/* We failed to read the EDID, so send an event for this. */
|
||||
ed.present = false;
|
||||
ed.segment = adv7511_rd(sd, 0xc4);
|
||||
ed.phys_addr = CEC_PHYS_ADDR_INVALID;
|
||||
cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
|
||||
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
|
||||
v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
|
||||
}
|
||||
@@ -1406,13 +1702,16 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments);
|
||||
state->edid.complete = true;
|
||||
|
||||
ed.phys_addr = cec_get_edid_phys_addr(state->edid.data,
|
||||
state->edid.segments * 256,
|
||||
NULL);
|
||||
/* report when we have all segments
|
||||
but report only for segment 0
|
||||
*/
|
||||
ed.present = true;
|
||||
ed.segment = 0;
|
||||
state->edid_detect_counter++;
|
||||
cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
|
||||
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
|
||||
return ed.present;
|
||||
}
|
||||
@@ -1420,17 +1719,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int adv7511_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv7511_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv7511_int_ops = {
|
||||
.registered = adv7511_registered,
|
||||
.unregistered = adv7511_unregistered,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* Setup ADV7511 */
|
||||
static void adv7511_init_setup(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct adv7511_state_edid *edid = &state->edid;
|
||||
u32 cec_clk = state->pdata.cec_clk;
|
||||
u8 ratio;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s\n", __func__);
|
||||
|
||||
/* clear all interrupts */
|
||||
adv7511_wr(sd, 0x96, 0xff);
|
||||
adv7511_wr(sd, 0x97, 0xff);
|
||||
/*
|
||||
* Stop HPD from resetting a lot of registers.
|
||||
* It might leave the chip in a partly un-initialized state,
|
||||
@@ -1442,6 +1767,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
|
||||
adv7511_set_isr(sd, false);
|
||||
adv7511_s_stream(sd, false);
|
||||
adv7511_s_audio_stream(sd, false);
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk);
|
||||
|
||||
/* cec soft reset */
|
||||
adv7511_cec_write(sd, 0x50, 0x01);
|
||||
adv7511_cec_write(sd, 0x50, 0x00);
|
||||
|
||||
/* legacy mode */
|
||||
adv7511_cec_write(sd, 0x4a, 0x00);
|
||||
|
||||
if (cec_clk % 750000 != 0)
|
||||
v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n",
|
||||
__func__, cec_clk);
|
||||
|
||||
ratio = (cec_clk / 750000) - 1;
|
||||
adv7511_cec_write(sd, 0x4e, ratio << 2);
|
||||
}
|
||||
|
||||
static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
@@ -1476,6 +1820,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
||||
client->addr << 1);
|
||||
|
||||
v4l2_i2c_subdev_init(sd, client, &adv7511_ops);
|
||||
sd->internal_ops = &adv7511_int_ops;
|
||||
|
||||
hdl = &state->hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 10);
|
||||
@@ -1516,26 +1861,47 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
||||
chip_id[0] = adv7511_rd(sd, 0xf5);
|
||||
chip_id[1] = adv7511_rd(sd, 0xf6);
|
||||
if (chip_id[0] != 0x75 || chip_id[1] != 0x11) {
|
||||
v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]);
|
||||
v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0],
|
||||
chip_id[1]);
|
||||
err = -EIO;
|
||||
goto err_entity;
|
||||
}
|
||||
|
||||
state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
|
||||
state->i2c_edid = i2c_new_dummy(client->adapter,
|
||||
state->i2c_edid_addr >> 1);
|
||||
if (state->i2c_edid == NULL) {
|
||||
v4l2_err(sd, "failed to register edid i2c client\n");
|
||||
err = -ENOMEM;
|
||||
goto err_entity;
|
||||
}
|
||||
|
||||
adv7511_wr(sd, 0xe1, state->i2c_cec_addr);
|
||||
if (state->pdata.cec_clk < 3000000 ||
|
||||
state->pdata.cec_clk > 100000000) {
|
||||
v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n",
|
||||
__func__, state->pdata.cec_clk);
|
||||
state->pdata.cec_clk = 0;
|
||||
}
|
||||
|
||||
if (state->pdata.cec_clk) {
|
||||
state->i2c_cec = i2c_new_dummy(client->adapter,
|
||||
state->i2c_cec_addr >> 1);
|
||||
if (state->i2c_cec == NULL) {
|
||||
v4l2_err(sd, "failed to register cec i2c client\n");
|
||||
goto err_unreg_edid;
|
||||
}
|
||||
adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */
|
||||
} else {
|
||||
adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
|
||||
}
|
||||
|
||||
state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1);
|
||||
if (state->i2c_pktmem == NULL) {
|
||||
v4l2_err(sd, "failed to register pktmem i2c client\n");
|
||||
err = -ENOMEM;
|
||||
goto err_unreg_edid;
|
||||
goto err_unreg_cec;
|
||||
}
|
||||
|
||||
adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
|
||||
state->work_queue = create_singlethread_workqueue(sd->name);
|
||||
if (state->work_queue == NULL) {
|
||||
v4l2_err(sd, "could not create workqueue\n");
|
||||
@@ -1546,6 +1912,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
||||
INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler);
|
||||
|
||||
adv7511_init_setup(sd);
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
|
||||
state, dev_name(&client->dev), CEC_CAP_TRANSMIT |
|
||||
CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
|
||||
ADV7511_MAX_ADDRS, &client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err) {
|
||||
destroy_workqueue(state->work_queue);
|
||||
goto err_unreg_pktmem;
|
||||
}
|
||||
#endif
|
||||
|
||||
adv7511_set_isr(sd, true);
|
||||
adv7511_check_monitor_present_status(sd);
|
||||
|
||||
@@ -1555,6 +1934,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
||||
|
||||
err_unreg_pktmem:
|
||||
i2c_unregister_device(state->i2c_pktmem);
|
||||
err_unreg_cec:
|
||||
if (state->i2c_cec)
|
||||
i2c_unregister_device(state->i2c_cec);
|
||||
err_unreg_edid:
|
||||
i2c_unregister_device(state->i2c_edid);
|
||||
err_entity:
|
||||
@@ -1576,9 +1958,12 @@ static int adv7511_remove(struct i2c_client *client)
|
||||
v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
adv7511_set_isr(sd, false);
|
||||
adv7511_init_setup(sd);
|
||||
cancel_delayed_work(&state->edid_handler);
|
||||
i2c_unregister_device(state->i2c_edid);
|
||||
if (state->i2c_cec)
|
||||
i2c_unregister_device(state->i2c_cec);
|
||||
i2c_unregister_device(state->i2c_pktmem);
|
||||
destroy_workqueue(state->work_queue);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <media/i2c/adv7604.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-event.h>
|
||||
@@ -80,6 +81,8 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
#define ADV76XX_OP_SWAP_CB_CR (1 << 0)
|
||||
|
||||
#define ADV76XX_MAX_ADDRS (3)
|
||||
|
||||
enum adv76xx_type {
|
||||
ADV7604,
|
||||
ADV7611,
|
||||
@@ -188,6 +191,12 @@ struct adv76xx_state {
|
||||
struct delayed_work delayed_work_enable_hotplug;
|
||||
bool restart_stdi_once;
|
||||
|
||||
/* CEC */
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV76XX_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
|
||||
/* i2c clients */
|
||||
struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
|
||||
|
||||
@@ -381,7 +390,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val);
|
||||
}
|
||||
|
||||
static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
@@ -414,6 +424,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
|
||||
}
|
||||
|
||||
static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
||||
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
@@ -892,9 +908,9 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
const struct adv76xx_chip_info *info = state->info;
|
||||
u16 cable_det = info->read_cable_det(sd);
|
||||
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
|
||||
info->read_cable_det(sd));
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
|
||||
}
|
||||
|
||||
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
|
||||
@@ -1924,6 +1940,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
if ((cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x02) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
|
||||
__func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
}
|
||||
if (tx_raw_status & 0x04) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x01) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
u8 cec_irq;
|
||||
|
||||
/* cec controller */
|
||||
cec_irq = io_read(sd, 0x4d) & 0x0f;
|
||||
if (!cec_irq)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
|
||||
adv76xx_cec_tx_raw_status(sd, cec_irq);
|
||||
if (cec_irq & 0x08) {
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = cec_read(sd, 0x25) & 0x1f;
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = cec_read(sd, i + 0x15);
|
||||
cec_write(sd, 0x26, 0x01); /* re-enable rx */
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* note: the bit order is swapped between 0x4d and 0x4e */
|
||||
cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
|
||||
((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
|
||||
io_write(sd, 0x4e, cec_irq);
|
||||
|
||||
if (handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
|
||||
cec_write(sd, 0x2c, 0x01); /* cec soft reset */
|
||||
cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready */
|
||||
io_write_clr_set(sd, 0x50, 0x0f, 0x0f);
|
||||
cec_write(sd, 0x26, 0x01); /* enable rx */
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
/* disable cec interrupts */
|
||||
io_write_clr_set(sd, 0x50, 0x0f, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0x00);
|
||||
/* power down cec section */
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
adv76xx_s_detect_tx_5v_ctrl(sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV76XX_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV76XX_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV76XX_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV76XX_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
cec_write_clr_set(sd, 0x27, 0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
cec_write_clr_set(sd, 0x28, 0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
cec_write_clr_set(sd, 0x27, 0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
cec_write_clr_set(sd, 0x27, 0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x29, 0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv76xx_cec_adap_ops = {
|
||||
.adap_enable = adv76xx_cec_adap_enable,
|
||||
.adap_log_addr = adv76xx_cec_adap_log_addr,
|
||||
.adap_transmit = adv76xx_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
@@ -1969,6 +2189,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
/* cec */
|
||||
adv76xx_cec_isr(sd, handled);
|
||||
#endif
|
||||
|
||||
/* tx 5v detect */
|
||||
tx_5v = irq_reg_0x70 & info->cable_det_mask;
|
||||
if (tx_5v) {
|
||||
@@ -2018,39 +2243,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_edid_spa_location(const u8 *edid)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
if ((edid[0x7e] != 1) ||
|
||||
(edid[0x80] != 0x02) ||
|
||||
(edid[0x81] != 0x03)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* search Vendor Specific Data Block (tag 3) */
|
||||
d = edid[0x82] & 0x7f;
|
||||
if (d > 4) {
|
||||
int i = 0x84;
|
||||
int end = 0x80 + d;
|
||||
|
||||
do {
|
||||
u8 tag = edid[i] >> 5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if ((tag == 3) && (len >= 5))
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
const struct adv76xx_chip_info *info = state->info;
|
||||
int spa_loc;
|
||||
unsigned int spa_loc;
|
||||
u16 pa;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
@@ -2081,6 +2279,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
edid->blocks = 2;
|
||||
return -E2BIG;
|
||||
}
|
||||
pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
|
||||
err = cec_phys_addr_validate(pa, &pa, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
|
||||
__func__, edid->pad, state->edid.present);
|
||||
@@ -2090,9 +2292,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
adv76xx_set_hpd(state, 0);
|
||||
rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
|
||||
|
||||
spa_loc = get_edid_spa_location(edid->edid);
|
||||
if (spa_loc < 0)
|
||||
spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
|
||||
/*
|
||||
* Return an error if no location of the source physical address
|
||||
* was found.
|
||||
*/
|
||||
if (spa_loc == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (edid->pad) {
|
||||
case ADV76XX_PAD_HDMI_PORT_A:
|
||||
@@ -2152,6 +2357,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 100 ms */
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
@@ -2275,8 +2481,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
|
||||
((edid_enabled & 0x02) ? "Yes" : "No"),
|
||||
((edid_enabled & 0x04) ? "Yes" : "No"),
|
||||
((edid_enabled & 0x08) ? "Yes" : "No"));
|
||||
v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
v4l2_info(sd, "-----Signal status-----\n");
|
||||
cable_det = info->read_cable_det(sd);
|
||||
@@ -2386,6 +2603,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
|
||||
}
|
||||
}
|
||||
|
||||
static int adv76xx_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv76xx_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
|
||||
@@ -2429,6 +2664,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
|
||||
.pad = &adv76xx_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
|
||||
.registered = adv76xx_registered,
|
||||
.unregistered = adv76xx_unregistered,
|
||||
};
|
||||
|
||||
/* -------------------------- custom ctrls ---------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
|
||||
@@ -3111,6 +3351,7 @@ static int adv76xx_probe(struct i2c_client *client,
|
||||
id->name, i2c_adapter_id(client->adapter),
|
||||
client->addr);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->internal_ops = &adv76xx_int_ops;
|
||||
|
||||
/* Configure IO Regmap region */
|
||||
err = configure_regmap(state, ADV76XX_PAGE_IO);
|
||||
@@ -3246,6 +3487,18 @@ static int adv76xx_probe(struct i2c_client *client,
|
||||
err = adv76xx_core_init(sd);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
|
||||
state, dev_name(&client->dev),
|
||||
CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
|
||||
&client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
#endif
|
||||
|
||||
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
@@ -3273,6 +3526,13 @@ static int adv76xx_remove(struct i2c_client *client)
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
/* disable interrupts */
|
||||
io_write(sd, 0x40, 0);
|
||||
io_write(sd, 0x41, 0);
|
||||
io_write(sd, 0x46, 0);
|
||||
io_write(sd, 0x6e, 0);
|
||||
io_write(sd, 0x73, 0);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
v4l2_async_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/v4l2-dv-timings.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
@@ -79,6 +80,8 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
#define ADV7842_OP_SWAP_CB_CR (1 << 0)
|
||||
|
||||
#define ADV7842_MAX_ADDRS (3)
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
@@ -141,6 +144,11 @@ struct adv7842_state {
|
||||
struct v4l2_ctrl *free_run_color_ctrl_manual;
|
||||
struct v4l2_ctrl *free_run_color_ctrl;
|
||||
struct v4l2_ctrl *rgb_quantization_range_ctrl;
|
||||
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7842_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
};
|
||||
|
||||
/* Unsupported timings. This device cannot support 720p30. */
|
||||
@@ -417,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
|
||||
}
|
||||
|
||||
static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
||||
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
|
||||
@@ -695,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd)
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static u16 adv7842_read_cable_det(struct v4l2_subdev *sd)
|
||||
{
|
||||
u8 reg = io_read(sd, 0x6f);
|
||||
u16 val = 0;
|
||||
|
||||
if (reg & 0x02)
|
||||
val |= 1; /* port A */
|
||||
if (reg & 0x01)
|
||||
val |= 2; /* port B */
|
||||
return val;
|
||||
}
|
||||
|
||||
static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
@@ -760,50 +780,18 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edid_spa_location(const u8 *edid)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
/*
|
||||
* TODO, improve and update for other CEA extensions
|
||||
* currently only for 1 segment (256 bytes),
|
||||
* i.e. 1 extension block and CEA revision 3.
|
||||
*/
|
||||
if ((edid[0x7e] != 1) ||
|
||||
(edid[0x80] != 0x02) ||
|
||||
(edid[0x81] != 0x03)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* search Vendor Specific Data Block (tag 3)
|
||||
*/
|
||||
d = edid[0x82] & 0x7f;
|
||||
if (d > 4) {
|
||||
int i = 0x84;
|
||||
int end = 0x80 + d;
|
||||
do {
|
||||
u8 tag = edid[i]>>5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if ((tag == 3) && (len >= 5))
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
const u8 *val = state->hdmi_edid.edid;
|
||||
int spa_loc = edid_spa_location(val);
|
||||
const u8 *edid = state->hdmi_edid.edid;
|
||||
int spa_loc;
|
||||
u16 pa;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
|
||||
/* HPA disable on port A and B */
|
||||
io_write_and_or(sd, 0x20, 0xcf, 0x00);
|
||||
@@ -814,24 +802,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
if (!state->hdmi_edid.present)
|
||||
return 0;
|
||||
|
||||
pa = cec_get_edid_phys_addr(edid, 256, &spa_loc);
|
||||
err = cec_phys_addr_validate(pa, &pa, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Return an error if no location of the source physical address
|
||||
* was found.
|
||||
*/
|
||||
if (spa_loc == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* edid segment pointer '0' for HDMI ports */
|
||||
rep_write_and_or(sd, 0x77, 0xef, 0x00);
|
||||
|
||||
for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
|
||||
err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
|
||||
I2C_SMBUS_BLOCK_MAX, val + i);
|
||||
I2C_SMBUS_BLOCK_MAX, edid + i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (spa_loc < 0)
|
||||
spa_loc = 0xc0; /* Default value [REF_02, p. 199] */
|
||||
|
||||
if (port == ADV7842_EDID_PORT_A) {
|
||||
rep_write(sd, 0x72, val[spa_loc]);
|
||||
rep_write(sd, 0x73, val[spa_loc + 1]);
|
||||
rep_write(sd, 0x72, edid[spa_loc]);
|
||||
rep_write(sd, 0x73, edid[spa_loc + 1]);
|
||||
} else {
|
||||
rep_write(sd, 0x74, val[spa_loc]);
|
||||
rep_write(sd, 0x75, val[spa_loc + 1]);
|
||||
rep_write(sd, 0x74, edid[spa_loc]);
|
||||
rep_write(sd, 0x75, edid[spa_loc + 1]);
|
||||
}
|
||||
rep_write(sd, 0x76, spa_loc & 0xff);
|
||||
rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
|
||||
@@ -851,6 +848,7 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 200 ms */
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
@@ -980,20 +978,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd,
|
||||
static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl);
|
||||
u8 reg_io_6f = io_read(sd, 0x6f);
|
||||
int val = 0;
|
||||
u16 cable_det = adv7842_read_cable_det(sd);
|
||||
|
||||
if (reg_io_6f & 0x02)
|
||||
val |= 1; /* port A */
|
||||
if (reg_io_6f & 0x01)
|
||||
val |= 2; /* port B */
|
||||
v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val);
|
||||
|
||||
if (val != prev)
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val);
|
||||
return 0;
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
|
||||
}
|
||||
|
||||
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
|
||||
@@ -2167,6 +2156,207 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
if ((cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x02) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
|
||||
__func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x04) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x01) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled)
|
||||
{
|
||||
u8 cec_irq;
|
||||
|
||||
/* cec controller */
|
||||
cec_irq = io_read(sd, 0x93) & 0x0f;
|
||||
if (!cec_irq)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
|
||||
adv7842_cec_tx_raw_status(sd, cec_irq);
|
||||
if (cec_irq & 0x08) {
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = cec_read(sd, 0x25) & 0x1f;
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = cec_read(sd, i + 0x15);
|
||||
cec_write(sd, 0x26, 0x01); /* re-enable rx */
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
io_write(sd, 0x94, cec_irq);
|
||||
|
||||
if (handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
|
||||
cec_write(sd, 0x2c, 0x01); /* cec soft reset */
|
||||
cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready */
|
||||
io_write_clr_set(sd, 0x96, 0x0f, 0x0f);
|
||||
cec_write(sd, 0x26, 0x01); /* enable rx */
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
/* disable cec interrupts */
|
||||
io_write_clr_set(sd, 0x96, 0x0f, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0x00);
|
||||
/* power down cec section */
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV7842_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7842_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7842_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7842_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
cec_write_clr_set(sd, 0x27, 0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
cec_write_clr_set(sd, 0x28, 0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
cec_write_clr_set(sd, 0x27, 0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
cec_write_clr_set(sd, 0x27, 0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x29, 0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7842_cec_adap_ops = {
|
||||
.adap_enable = adv7842_cec_adap_enable,
|
||||
.adap_log_addr = adv7842_cec_adap_log_addr,
|
||||
.adap_transmit = adv7842_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
@@ -2238,6 +2428,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
/* cec */
|
||||
adv7842_cec_isr(sd, handled);
|
||||
#endif
|
||||
|
||||
/* tx 5v detect */
|
||||
if (irq_status[2] & 0x3) {
|
||||
v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
|
||||
@@ -2318,10 +2513,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
|
||||
case ADV7842_EDID_PORT_A:
|
||||
case ADV7842_EDID_PORT_B:
|
||||
memset(&state->hdmi_edid.edid, 0, 256);
|
||||
if (e->blocks)
|
||||
if (e->blocks) {
|
||||
state->hdmi_edid.present |= 0x04 << e->pad;
|
||||
else
|
||||
} else {
|
||||
state->hdmi_edid.present &= ~(0x04 << e->pad);
|
||||
adv7842_s_detect_tx_5v_ctrl(sd);
|
||||
}
|
||||
memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
|
||||
err = edid_write_hdmi_segment(sd, e->pad);
|
||||
break;
|
||||
@@ -2509,8 +2706,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
|
||||
v4l2_info(sd, "HPD A %s, B %s\n",
|
||||
reg_io_0x21 & 0x02 ? "enabled" : "disabled",
|
||||
reg_io_0x21 & 0x01 ? "enabled" : "disabled");
|
||||
v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
v4l2_info(sd, "-----Signal status-----\n");
|
||||
if (state->hdmi_port_a) {
|
||||
@@ -3031,6 +3239,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd,
|
||||
}
|
||||
}
|
||||
|
||||
static int adv7842_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv7842_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
|
||||
@@ -3077,6 +3303,11 @@ static const struct v4l2_subdev_ops adv7842_ops = {
|
||||
.pad = &adv7842_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv7842_int_ops = {
|
||||
.registered = adv7842_registered,
|
||||
.unregistered = adv7842_unregistered,
|
||||
};
|
||||
|
||||
/* -------------------------- custom ctrls ---------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = {
|
||||
@@ -3241,6 +3472,7 @@ static int adv7842_probe(struct i2c_client *client,
|
||||
sd = &state->sd;
|
||||
v4l2_i2c_subdev_init(sd, client, &adv7842_ops);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->internal_ops = &adv7842_int_ops;
|
||||
state->mode = pdata->mode;
|
||||
|
||||
state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A;
|
||||
@@ -3324,6 +3556,17 @@ static int adv7842_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
goto err_entity;
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops,
|
||||
state, dev_name(&client->dev),
|
||||
CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS,
|
||||
&client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
#endif
|
||||
|
||||
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
return 0;
|
||||
@@ -3347,7 +3590,6 @@ static int adv7842_remove(struct i2c_client *client)
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
adv7842_irq_enable(sd, false);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
Reference in New Issue
Block a user