diff options
Diffstat (limited to 'drivers/media/i2c/adv7604.c')
| -rw-r--r-- | drivers/media/i2c/adv7604.c | 559 |
1 files changed, 367 insertions, 192 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 660bacb8f7d9..516553fb17e9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * adv7604 - Analog Devices ADV7604 video decoder driver * * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ /* @@ -39,6 +27,7 @@ #include <linux/videodev2.h> #include <linux/workqueue.h> #include <linux/regmap.h> +#include <linux/interrupt.h> #include <media/i2c/adv7604.h> #include <media/cec.h> @@ -52,8 +41,8 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); -MODULE_DESCRIPTION("Analog Devices ADV7604 video decoder driver"); -MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); +MODULE_DESCRIPTION("Analog Devices ADV7604/10/11/12 video decoder driver"); +MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>"); MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>"); MODULE_LICENSE("GPL"); @@ -84,9 +73,11 @@ MODULE_LICENSE("GPL"); #define ADV76XX_MAX_ADDRS (3) +#define ADV76XX_MAX_EDID_BLOCKS 4 + enum adv76xx_type { ADV7604, - ADV7611, + ADV7611, // including ADV7610 ADV7612, }; @@ -119,6 +110,11 @@ struct adv76xx_chip_info { unsigned int edid_enable_reg; unsigned int edid_status_reg; + unsigned int edid_segment_reg; + unsigned int edid_segment_mask; + unsigned int edid_spa_loc_reg; + unsigned int edid_spa_loc_msb_mask; + unsigned int edid_spa_port_b_reg; unsigned int lcf_reg; unsigned int cable_det_mask; @@ -126,6 +122,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; @@ -182,7 +183,7 @@ struct adv76xx_state { const struct adv76xx_format_info *format; struct { - u8 edid[256]; + u8 edid[ADV76XX_MAX_EDID_BLOCKS * 128]; u32 present; unsigned blocks; } edid; @@ -192,6 +193,9 @@ struct adv76xx_state { struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; + struct dentry *debugfs_dir; + struct v4l2_debugfs_if *infoframes; + /* CEC */ struct cec_adapter *cec_adap; u8 cec_addr[ADV76XX_MAX_ADDRS]; @@ -397,14 +401,14 @@ static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); } -static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) +static inline int __always_unused avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg); } -static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) +static inline int __always_unused avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); @@ -438,14 +442,14 @@ static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg); } -static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) +static inline int __always_unused infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val); } -static inline int afe_read(struct v4l2_subdev *sd, u8 reg) +static inline int __always_unused afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); @@ -478,14 +482,14 @@ static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val); } -static inline int edid_read(struct v4l2_subdev *sd, u8 reg) +static inline int __always_unused edid_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg); } -static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) +static inline int __always_unused edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); @@ -518,10 +522,17 @@ static inline int edid_write_block(struct v4l2_subdev *sd, static void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd) { + const struct adv76xx_chip_info *info = state->info; unsigned int i; - for (i = 0; i < state->info->num_dv_ports; ++i) - gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i)); + if (info->type == ADV7604) { + for (i = 0; i < state->info->num_dv_ports; ++i) + gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i)); + } else { + for (i = 0; i < state->info->num_dv_ports; ++i) + io_write_clr_set(&state->sd, 0x20, 0x80 >> i, + (!!(hpd & BIT(i))) << (7 - i)); + } v4l2_subdev_notify(&state->sd, ADV76XX_HOTPLUG, &hpd); } @@ -562,7 +573,7 @@ static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val); } -static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) +static inline int __always_unused test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); @@ -593,14 +604,14 @@ static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 v return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val); } -static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) +static inline int __always_unused vdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg); } -static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +static inline int __always_unused vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); @@ -618,7 +629,7 @@ static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg) unsigned int val; int err; - if (!(BIT(page) & state->info->page_mask)) + if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask)) return -EINVAL; reg &= 0xff; @@ -633,7 +644,7 @@ static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) struct adv76xx_state *state = to_state(sd); unsigned int page = reg >> 8; - if (!(BIT(page) & state->info->page_mask)) + if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask)) return -EINVAL; reg &= 0xff; @@ -778,7 +789,7 @@ static const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -789,7 +800,7 @@ static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 225000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -1397,12 +1408,13 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - false, timings)) + false, adv76xx_get_dv_timings_cap(sd, -1), timings)) return 0; if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - false, state->aspect_ratio, timings)) + false, state->aspect_ratio, + adv76xx_get_dv_timings_cap(sd, -1), timings)) return 0; v4l2_dbg(2, debug, sd, @@ -1509,23 +1521,14 @@ static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) { - unsigned int freq; int a, b; a = hdmi_read(sd, 0x06); b = hdmi_read(sd, 0x3b); if (a < 0 || b < 0) return 0; - freq = a * 1000000 + ((b & 0x30) >> 4) * 250000; - - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; - - freq = freq * 8 / bits_per_channel; - } - return freq; + return a * 1000000 + ((b & 0x30) >> 4) * 250000; } static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) @@ -1536,11 +1539,30 @@ static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) b = hdmi_read(sd, 0x52); if (a < 0 || b < 0) return 0; + return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; } -static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) +static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; + unsigned int freq, bits_per_channel, pixelrepetition; + + freq = info->read_hdmi_pixelclock(sd); + if (is_hdmi(sd)) { + /* adjust for deep color mode and pixel repetition */ + bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + pixelrepetition = (hdmi_read(sd, 0x05) & 0x0f) + 1; + + freq = freq * 8 / bits_per_channel / pixelrepetition; + } + + return freq; +} + +static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) { struct adv76xx_state *state = to_state(sd); const struct adv76xx_chip_info *info = state->info; @@ -1585,7 +1607,7 @@ static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, bt->width = w; bt->height = h; - bt->pixelclock = info->read_hdmi_pixelclock(sd); + bt->pixelclock = adv76xx_read_hdmi_pixelclock(sd); bt->hfrontporch = hdmi_read16(sd, 0x20, info->hfrontporch_mask); bt->hsync = hdmi_read16(sd, 0x22, info->hsync_mask); bt->hbackporch = hdmi_read16(sd, 0x24, info->hbackporch_mask); @@ -1669,8 +1691,8 @@ found: return 0; } -static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) +static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) { struct adv76xx_state *state = to_state(sd); struct v4l2_bt_timings *bt; @@ -1712,8 +1734,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, return 0; } -static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) +static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) { struct adv76xx_state *state = to_state(sd); @@ -1787,6 +1809,9 @@ static void select_input(struct v4l2_subdev *sd) v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", __func__, state->selected_input); } + + /* Enable video adjustment (contrast, saturation, brightness and hue) */ + cp_write_clr_set(sd, 0x3e, 0x80, 0x80); } static int adv76xx_s_routing(struct v4l2_subdev *sd, @@ -1815,7 +1840,7 @@ static int adv76xx_s_routing(struct v4l2_subdev *sd, } static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct adv76xx_state *state = to_state(sd); @@ -1895,7 +1920,7 @@ static void adv76xx_setup_format(struct adv76xx_state *state) } static int adv76xx_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1908,7 +1933,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1918,7 +1943,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, } static int adv76xx_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct adv76xx_state *state = to_state(sd); @@ -1938,7 +1963,7 @@ static int adv76xx_get_selection(struct v4l2_subdev *sd, } static int adv76xx_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1948,7 +1973,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, return -EINVAL; info = adv76xx_format_info(state, format->format.code); - if (info == NULL) + if (!info) info = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); adv76xx_fill_format(state, &format->format); @@ -1957,7 +1982,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; @@ -1982,6 +2007,7 @@ static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) __func__); cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); + return; } if (tx_raw_status & 0x04) { u8 status; @@ -2014,10 +2040,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; @@ -2027,23 +2054,29 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) struct cec_msg msg; msg.len = cec_read(sd, 0x25) & 0x1f; - if (msg.len > 16) - msg.len = 16; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; 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_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; @@ -2052,6 +2085,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) { @@ -2063,11 +2097,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 */ @@ -2232,6 +2266,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); @@ -2256,7 +2300,7 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return 0; } - if (data == NULL) + if (!data) return -ENODATA; if (edid->start_block >= state->edid.blocks) @@ -2275,7 +2319,7 @@ 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; unsigned int spa_loc; - u16 pa; + u16 pa, parent_pa; int err; int i; @@ -2295,22 +2339,34 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) state->aspect_ratio.numerator = 16; state->aspect_ratio.denominator = 9; - if (!state->edid.present) + if (!state->edid.present) { state->edid.blocks = 0; + cec_phys_addr_invalidate(state->cec_adap); + } v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); return 0; } - if (edid->blocks > 2) { - edid->blocks = 2; + if (edid->blocks > ADV76XX_MAX_EDID_BLOCKS) { + edid->blocks = ADV76XX_MAX_EDID_BLOCKS; return -E2BIG; } - pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); - err = cec_phys_addr_validate(pa, &pa, NULL); + + pa = v4l2_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); + err = v4l2_phys_addr_validate(pa, &parent_pa, NULL); if (err) return err; + if (!spa_loc) { + /* + * There is no SPA, so just set spa_loc to 128 and pa to whatever + * data is there. + */ + spa_loc = 128; + pa = (edid->edid[spa_loc] << 8) | edid->edid[spa_loc + 1]; + } + v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); @@ -2319,41 +2375,33 @@ 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); - /* - * 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: - state->spa_port_a[0] = edid->edid[spa_loc]; - state->spa_port_a[1] = edid->edid[spa_loc + 1]; + state->spa_port_a[0] = pa >> 8; + state->spa_port_a[1] = pa & 0xff; break; case ADV7604_PAD_HDMI_PORT_B: - rep_write(sd, 0x70, edid->edid[spa_loc]); - rep_write(sd, 0x71, edid->edid[spa_loc + 1]); + rep_write(sd, info->edid_spa_port_b_reg, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 1, pa & 0xff); break; case ADV7604_PAD_HDMI_PORT_C: - rep_write(sd, 0x72, edid->edid[spa_loc]); - rep_write(sd, 0x73, edid->edid[spa_loc + 1]); + rep_write(sd, info->edid_spa_port_b_reg + 2, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 3, pa & 0xff); break; case ADV7604_PAD_HDMI_PORT_D: - rep_write(sd, 0x74, edid->edid[spa_loc]); - rep_write(sd, 0x75, edid->edid[spa_loc + 1]); + rep_write(sd, info->edid_spa_port_b_reg + 4, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 5, pa & 0xff); break; default: return -EINVAL; } - if (info->type == ADV7604) { - rep_write(sd, 0x76, spa_loc & 0xff); - rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2); - } else { - /* ADV7612 Software Manual Rev. A, p. 15 */ - rep_write(sd, 0x70, spa_loc & 0xff); - rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); + if (info->edid_spa_loc_reg) { + u8 mask = info->edid_spa_loc_msb_mask; + + rep_write(sd, info->edid_spa_loc_reg, spa_loc & 0xff); + rep_write_clr_set(sd, info->edid_spa_loc_reg + 1, + mask, (spa_loc & 0x100) ? mask : 0); } edid->edid[spa_loc] = state->spa_port_a[0]; @@ -2365,11 +2413,25 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) edid->edid[0x16]); state->edid.present |= 1 << edid->pad; - err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); + rep_write_clr_set(sd, info->edid_segment_reg, + info->edid_segment_mask, 0); + err = edid_write_block(sd, 128 * min(edid->blocks, 2U), state->edid.edid); if (err < 0) { v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); return err; } + if (edid->blocks > 2) { + rep_write_clr_set(sd, info->edid_segment_reg, + info->edid_segment_mask, + info->edid_segment_mask); + err = edid_write_block(sd, 128 * (edid->blocks - 2), + state->edid.edid + 256); + if (err < 0) { + v4l2_err(sd, "error %d writing edid pad %d\n", + err, edid->pad); + return err; + } + } /* adv76xx calculates the checksums and enables I2C access to internal EDID RAM from DDC port. */ @@ -2384,10 +2446,10 @@ 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); + cec_s_phys_addr(state->cec_adap, parent_pa, false); - /* enable hotplug after 100 ms */ - schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); + /* enable hotplug after 143 ms */ + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7); return 0; } @@ -2400,10 +2462,9 @@ static const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = { { "Vendor", 0x10, 0xec, 0x54 } }; -static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, - union hdmi_infoframe *frame) +static int adv76xx_read_infoframe_buf(struct v4l2_subdev *sd, int index, + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN]) { - uint8_t buffer[32]; u8 len; int i; @@ -2414,27 +2475,20 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, } for (i = 0; i < 3; i++) - buffer[i] = infoframe_read(sd, - adv76xx_cri[index].head_addr + i); + buf[i] = infoframe_read(sd, adv76xx_cri[index].head_addr + i); - len = buffer[2] + 1; + len = buf[2] + 1; - if (len + 3 > sizeof(buffer)) { + if (len + 3 > V4L2_DEBUGFS_IF_MAX_LEN) { v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, adv76xx_cri[index].desc, len); return -ENOENT; } for (i = 0; i < len; i++) - buffer[i + 3] = infoframe_read(sd, - adv76xx_cri[index].payload_addr + i); - - if (hdmi_infoframe_unpack(frame, buffer) < 0) { - v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, - adv76xx_cri[index].desc); - return -ENOENT; - } - return 0; + buf[i + 3] = infoframe_read(sd, + adv76xx_cri[index].payload_addr + i); + return len + 3; } static void adv76xx_log_infoframes(struct v4l2_subdev *sd) @@ -2447,12 +2501,20 @@ static void adv76xx_log_infoframes(struct v4l2_subdev *sd) } for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) { - union hdmi_infoframe frame; struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + union hdmi_infoframe frame; + int len; + + len = adv76xx_read_infoframe_buf(sd, i, buffer); + if (len < 0) + continue; - if (adv76xx_read_infoframe(sd, i, &frame)) - return; - hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); + if (hdmi_infoframe_unpack(&frame, buffer, len) < 0) + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", + __func__, adv76xx_cri[i].desc); + else + hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); } } @@ -2462,10 +2524,10 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) const struct adv76xx_chip_info *info = state->info; struct v4l2_dv_timings timings; struct stdi_readback stdi; - u8 reg_io_0x02 = io_read(sd, 0x02); + int ret; + u8 reg_io_0x02; u8 edid_enabled; u8 cable_det; - static const char * const csc_coeff_sel_rb[16] = { "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", @@ -2485,7 +2547,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", "xvYCC Bt.601", "xvYCC Bt.709", "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", - "sYCC", "Adobe YCC 601", "AdobeRGB", "invalid", "invalid", + "sYCC", "opYCC 601", "opRGB", "invalid", "invalid", "invalid", "invalid", "invalid" }; static const char * const rgb_quantization_range_txt[] = { @@ -2550,7 +2612,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) stdi.lcf, stdi.bl, stdi.lcvs, stdi.interlaced ? "interlaced" : "progressive", stdi.hs_pol, stdi.vs_pol); - if (adv76xx_query_dv_timings(sd, &timings)) + if (adv76xx_query_dv_timings(sd, 0, &timings)) v4l2_info(sd, "No video detected\n"); else v4l2_print_dv_timings(sd->name, "Detected format: ", @@ -2564,13 +2626,21 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Color space-----\n"); v4l2_info(sd, "RGB quantization range ctrl: %s\n", rgb_quantization_range_txt[state->rgb_quantization_range]); - v4l2_info(sd, "Input color space: %s\n", - input_color_space_txt[reg_io_0x02 >> 4]); - v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n", - (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", - (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? - "(16-235)" : "(0-255)", - (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); + + ret = io_read(sd, 0x02); + if (ret < 0) { + v4l2_info(sd, "Can't read Input/Output color space\n"); + } else { + reg_io_0x02 = ret; + + v4l2_info(sd, "Input color space: %s\n", + input_color_space_txt[reg_io_0x02 >> 4]); + v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n", + (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", + (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? + "(16-235)" : "(0-255)", + (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); + } v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]); @@ -2629,6 +2699,41 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd, } } +static ssize_t +adv76xx_debugfs_if_read(u32 type, void *priv, struct file *filp, + char __user *ubuf, size_t count, loff_t *ppos) +{ + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + struct v4l2_subdev *sd = priv; + int index; + int len; + + if (!is_hdmi(sd)) + return 0; + + switch (type) { + case V4L2_DEBUGFS_IF_AVI: + index = 0; + break; + case V4L2_DEBUGFS_IF_AUDIO: + index = 1; + break; + case V4L2_DEBUGFS_IF_SPD: + index = 2; + break; + case V4L2_DEBUGFS_IF_HDMI: + index = 3; + break; + default: + return 0; + } + + len = adv76xx_read_infoframe_buf(sd, index, buf); + if (len > 0) + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + return len < 0 ? 0 : len; +} + static int adv76xx_registered(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -2636,9 +2741,16 @@ static int adv76xx_registered(struct v4l2_subdev *sd) int err; err = cec_register_adapter(state->cec_adap, &client->dev); - if (err) + if (err) { cec_delete_adapter(state->cec_adap); - return err; + return err; + } + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); + state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, + V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO | + V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI, sd, + adv76xx_debugfs_if_read); + return 0; } static void adv76xx_unregistered(struct v4l2_subdev *sd) @@ -2646,6 +2758,10 @@ static void adv76xx_unregistered(struct v4l2_subdev *sd) struct adv76xx_state *state = to_state(sd); cec_unregister_adapter(state->cec_adap); + v4l2_debugfs_if_free(state->infoframes); + state->infoframes = NULL; + debugfs_remove_recursive(state->debugfs_dir); + state->debugfs_dir = NULL; } /* ----------------------------------------------------------------------- */ @@ -2669,9 +2785,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = { static const struct v4l2_subdev_video_ops adv76xx_video_ops = { .s_routing = adv76xx_s_routing, .g_input_status = adv76xx_g_input_status, - .s_dv_timings = adv76xx_s_dv_timings, - .g_dv_timings = adv76xx_g_dv_timings, - .query_dv_timings = adv76xx_query_dv_timings, }; static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { @@ -2681,6 +2794,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { .set_fmt = adv76xx_set_format, .get_edid = adv76xx_get_edid, .set_edid = adv76xx_set_edid, + .s_dv_timings = adv76xx_s_dv_timings, + .g_dv_timings = adv76xx_g_dv_timings, + .query_dv_timings = adv76xx_query_dv_timings, .dv_timings_cap = adv76xx_dv_timings_cap, .enum_dv_timings = adv76xx_enum_dv_timings, }; @@ -2733,6 +2849,27 @@ static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = { /* ----------------------------------------------------------------------- */ +struct adv76xx_register_map { + const char *name; + u8 default_addr; +}; + +static const struct adv76xx_register_map adv76xx_default_addresses[] = { + [ADV76XX_PAGE_IO] = { "main", 0x4c }, + [ADV7604_PAGE_AVLINK] = { "avlink", 0x42 }, + [ADV76XX_PAGE_CEC] = { "cec", 0x40 }, + [ADV76XX_PAGE_INFOFRAME] = { "infoframe", 0x3e }, + [ADV7604_PAGE_ESDP] = { "esdp", 0x38 }, + [ADV7604_PAGE_DPP] = { "dpp", 0x3c }, + [ADV76XX_PAGE_AFE] = { "afe", 0x26 }, + [ADV76XX_PAGE_REP] = { "rep", 0x32 }, + [ADV76XX_PAGE_EDID] = { "edid", 0x36 }, + [ADV76XX_PAGE_HDMI] = { "hdmi", 0x34 }, + [ADV76XX_PAGE_TEST] = { "test", 0x30 }, + [ADV76XX_PAGE_CP] = { "cp", 0x22 }, + [ADV7604_PAGE_VDP] = { "vdp", 0x24 }, +}; + static int adv76xx_core_init(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -2757,6 +2894,18 @@ static int adv76xx_core_init(struct v4l2_subdev *sd) io_write(sd, 0x0b, 0x44); /* Power down ESDP block */ cp_write(sd, 0xcf, 0x01); /* Power down macrovision */ + /* HPD */ + if (info->type != ADV7604) { + /* Set manual HPD values to 0 */ + io_write_clr_set(sd, 0x20, 0xc0, 0); + /* + * Set HPA_DELAY to 200 ms and set automatic HPD control + * to: internal EDID is active AND a cable is detected + * AND the manual HPD control is set to 1. + */ + hdmi_write_clr_set(sd, 0x6c, 0xf6, 0x26); + } + /* video format */ io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3); io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | @@ -2826,20 +2975,31 @@ static void adv76xx_unregister_clients(struct adv76xx_state *state) { unsigned int i; - for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) { - if (state->i2c_clients[i]) - i2c_unregister_device(state->i2c_clients[i]); - } + for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) + i2c_unregister_device(state->i2c_clients[i]); } static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, - u8 addr, u8 io_reg) + unsigned int page) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv76xx_state *state = to_state(sd); + struct adv76xx_platform_data *pdata = &state->pdata; + unsigned int io_reg = 0xf2 + page; + struct i2c_client *new_client; - if (addr) - io_write(sd, io_reg, addr << 1); - return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + if (pdata && pdata->i2c_addresses[page]) + new_client = i2c_new_dummy_device(client->adapter, + pdata->i2c_addresses[page]); + else + new_client = i2c_new_ancillary_device(client, + adv76xx_default_addresses[page].name, + adv76xx_default_addresses[page].default_addr); + + if (!IS_ERR(new_client)) + io_write(sd, io_reg, new_client->addr << 1); + + return new_client; } static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = { @@ -2930,11 +3090,20 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 4, .edid_enable_reg = 0x77, .edid_status_reg = 0x7d, + .edid_segment_reg = 0x77, + .edid_segment_mask = 0x10, + .edid_spa_loc_reg = 0x76, + .edid_spa_loc_msb_mask = 0x40, + .edid_spa_port_b_reg = 0x70, .lcf_reg = 0xb3, .tdms_lock_mask = 0xe0, .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, @@ -2976,11 +3145,16 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 1, .edid_enable_reg = 0x74, .edid_status_reg = 0x76, + .edid_segment_reg = 0x7a, + .edid_segment_mask = 0x01, .lcf_reg = 0xa3, .tdms_lock_mask = 0x43, .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, @@ -3017,11 +3191,19 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 1, /* normally 2 */ .edid_enable_reg = 0x74, .edid_status_reg = 0x76, + .edid_segment_reg = 0x7a, + .edid_segment_mask = 0x01, + .edid_spa_loc_reg = 0x70, + .edid_spa_loc_msb_mask = 0x01, + .edid_spa_port_b_reg = 0x52, .lcf_reg = 0xa3, .tdms_lock_mask = 0x43, .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, @@ -3055,6 +3237,7 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { static const struct i2c_device_id adv76xx_i2c_id[] = { { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, + { "adv7610", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } @@ -3062,6 +3245,7 @@ static const struct i2c_device_id adv76xx_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); static const struct of_device_id adv76xx_of_id[] __maybe_unused = { + { .compatible = "adi,adv7610", .data = &adv76xx_chip_info[ADV7611] }, { .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] }, { .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] }, { } @@ -3070,7 +3254,7 @@ MODULE_DEVICE_TABLE(of, adv76xx_of_id); static int adv76xx_parse_dt(struct adv76xx_state *state) { - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *endpoint; struct device_node *np; unsigned int flags; @@ -3079,18 +3263,15 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node; - /* Parse the endpoint. */ - endpoint = of_graph_get_next_endpoint(np, NULL); + /* FIXME: Parse the endpoint. */ + endpoint = of_graph_get_endpoint_by_regs(np, -1, -1); if (!endpoint) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg); - if (ret) { - of_node_put(endpoint); - return ret; - } - of_node_put(endpoint); + if (ret) + return ret; if (!of_property_read_u32(np, "default-input", &v)) state->pdata.default_input = v; @@ -3112,21 +3293,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; - - /* Use the default I2C addresses. */ - state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42; - state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40; - state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e; - state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38; - state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c; - state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26; - state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32; - state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36; - state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34; - state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30; - state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22; - state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24; + state->pdata.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH; /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; @@ -3296,9 +3463,9 @@ static void adv76xx_reset(struct adv76xx_state *state) } } -static int adv76xx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv76xx_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94; struct adv76xx_state *state; @@ -3316,10 +3483,8 @@ static int adv76xx_probe(struct i2c_client *client, client->addr << 1); state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); - if (!state) { - v4l_err(client, "Could not allocate adv76xx_state memory!\n"); + if (!state) return -ENOMEM; - } state->i2c_clients[ADV76XX_PAGE_IO] = client; @@ -3398,8 +3563,8 @@ static int adv76xx_probe(struct i2c_client *client, return -ENODEV; } if (val != 0x68) { - v4l2_err(sd, "not an adv7604 on address 0x%x\n", - client->addr << 1); + v4l2_err(sd, "not an ADV7604 on address 0x%x\n", + client->addr << 1); return -ENODEV; } break; @@ -3423,8 +3588,9 @@ static int adv76xx_probe(struct i2c_client *client, val |= val2; if ((state->info->type == ADV7611 && val != 0x2051) || (state->info->type == ADV7612 && val != 0x2041)) { - v4l2_err(sd, "not an adv761x on address 0x%x\n", - client->addr << 1); + v4l2_err(sd, "not an %s on address 0x%x\n", + state->info->type == ADV7611 ? "ADV7610/11" : "ADV7612", + client->addr << 1); return -ENODEV; } break; @@ -3441,7 +3607,7 @@ static int adv76xx_probe(struct i2c_client *client, v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_SATURATION, 0, 255, 1, 128); v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, - V4L2_CID_HUE, 0, 128, 1, 0); + V4L2_CID_HUE, 0, 255, 1, 0); ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); @@ -3476,17 +3642,19 @@ static int adv76xx_probe(struct i2c_client *client, } for (i = 1; i < ADV76XX_PAGE_MAX; ++i) { + struct i2c_client *dummy_client; + if (!(BIT(i) & state->info->page_mask)) continue; - state->i2c_clients[i] = - adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i], - 0xf2 + i); - if (state->i2c_clients[i] == NULL) { - err = -ENOMEM; + dummy_client = adv76xx_dummy_client(sd, i); + if (IS_ERR(dummy_client)) { + err = PTR_ERR(dummy_client); v4l2_err(sd, "failed to create i2c client %u\n", i); goto err_i2c; } + + state->i2c_clients[i] = dummy_client; } INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, @@ -3497,11 +3665,12 @@ static int adv76xx_probe(struct i2c_client *client, for (i = 0; i < state->source_pad; ++i) state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_DV_DECODER; err = media_entity_pads_init(&sd->entity, state->source_pad + 1, state->pads); if (err) - goto err_work_queues; + goto err_i2c; /* Configure regmaps */ err = configure_regmaps(state); @@ -3512,11 +3681,20 @@ 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), - CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | - CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS); + CEC_CAP_DEFAULTS, ADV76XX_MAX_ADDRS); err = PTR_ERR_OR_ZERO(state->cec_adap); if (err) goto err_entity; @@ -3533,8 +3711,6 @@ static int adv76xx_probe(struct i2c_client *client, err_entity: media_entity_cleanup(&sd->entity); -err_work_queues: - cancel_delayed_work(&state->delayed_work_enable_hotplug); err_i2c: adv76xx_unregister_clients(state); err_hdl: @@ -3544,7 +3720,7 @@ err_hdl: /* ----------------------------------------------------------------------- */ -static int adv76xx_remove(struct i2c_client *client) +static void adv76xx_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct adv76xx_state *state = to_state(sd); @@ -3556,12 +3732,11 @@ static int adv76xx_remove(struct i2c_client *client) io_write(sd, 0x6e, 0); io_write(sd, 0x73, 0); - cancel_delayed_work(&state->delayed_work_enable_hotplug); + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); adv76xx_unregister_clients(to_state(sd)); v4l2_ctrl_handler_free(sd->ctrl_handler); - return 0; } /* ----------------------------------------------------------------------- */ |
