From 40d91c9988af56d7a831df92c58fe28cebb3a764 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Oct 2018 07:30:02 -0400 Subject: [PATCH] media: adv7604: add CEC support for adv7611/adv7612 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CEC IP is very similar between the three HDMI receivers, but not identical. Add support for all three variants. Tested with an adv7604 and an adv7612. Signed-off-by: Hans Verkuil Reviewed-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 63 +++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 9eb7c70a7712..88786276dbe4 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -114,6 +114,11 @@ struct adv76xx_chip_info { unsigned int fmt_change_digital_mask; unsigned int cp_csc; + unsigned int cec_irq_status; + unsigned int cec_rx_enable; + unsigned int cec_rx_enable_mask; + bool cec_irq_swap; + const struct adv76xx_format_info *formats; unsigned int nformats; @@ -2003,10 +2008,11 @@ static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) { struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; u8 cec_irq; /* cec controller */ - cec_irq = io_read(sd, 0x4d) & 0x0f; + cec_irq = io_read(sd, info->cec_irq_status) & 0x0f; if (!cec_irq) return; @@ -2024,15 +2030,21 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) for (i = 0; i < msg.len; i++) msg.msg[i] = cec_read(sd, i + 0x15); - cec_write(sd, 0x26, 0x01); /* re-enable rx */ + cec_write(sd, info->cec_rx_enable, + info->cec_rx_enable_mask); /* 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 (info->cec_irq_swap) { + /* + * Note: the bit order is swapped between 0x4d and 0x4e + * on adv7604 + */ + cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | + ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); + } + io_write(sd, info->cec_irq_status + 1, cec_irq); if (handled) *handled = true; @@ -2041,6 +2053,7 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) { struct adv76xx_state *state = cec_get_drvdata(adap); + const struct adv76xx_chip_info *info = state->info; struct v4l2_subdev *sd = &state->sd; if (!state->cec_enabled_adap && enable) { @@ -2052,11 +2065,11 @@ static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) /* tx: arbitration lost */ /* tx: retry timeout */ /* rx: ready */ - io_write_clr_set(sd, 0x50, 0x0f, 0x0f); - cec_write(sd, 0x26, 0x01); /* enable rx */ + io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x0f); + cec_write(sd, info->cec_rx_enable, info->cec_rx_enable_mask); } else if (state->cec_enabled_adap && !enable) { /* disable cec interrupts */ - io_write_clr_set(sd, 0x50, 0x0f, 0x00); + io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x00); /* disable address mask 1-3 */ cec_write_clr_set(sd, 0x27, 0x70, 0x00); /* power down cec section */ @@ -2221,6 +2234,16 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static irqreturn_t adv76xx_irq_handler(int irq, void *dev_id) +{ + struct adv76xx_state *state = dev_id; + bool handled = false; + + adv76xx_isr(&state->sd, 0, &handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct adv76xx_state *state = to_state(sd); @@ -2960,6 +2983,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x1e, .fmt_change_digital_mask = 0xc1, .cp_csc = 0xfc, + .cec_irq_status = 0x4d, + .cec_rx_enable = 0x26, + .cec_rx_enable_mask = 0x01, + .cec_irq_swap = true, .formats = adv7604_formats, .nformats = ARRAY_SIZE(adv7604_formats), .set_termination = adv7604_set_termination, @@ -3006,6 +3033,9 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, .cp_csc = 0xf4, + .cec_irq_status = 0x93, + .cec_rx_enable = 0x2c, + .cec_rx_enable_mask = 0x02, .formats = adv7611_formats, .nformats = ARRAY_SIZE(adv7611_formats), .set_termination = adv7611_set_termination, @@ -3047,6 +3077,9 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, .cp_csc = 0xf4, + .cec_irq_status = 0x93, + .cec_rx_enable = 0x2c, + .cec_rx_enable_mask = 0x02, .formats = adv7612_formats, .nformats = ARRAY_SIZE(adv7612_formats), .set_termination = adv7611_set_termination, @@ -3134,7 +3167,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) state->pdata.insert_av_codes = 1; /* Disable the interrupt for now as no DT-based board uses it. */ - state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED; + state->pdata.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH; /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; @@ -3517,6 +3550,16 @@ static int adv76xx_probe(struct i2c_client *client, if (err) goto err_entity; + if (client->irq) { + err = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, adv76xx_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + client->name, state); + 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),