|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2018 Gateworks Corporation | 
|  | */ | 
|  | #include <linux/delay.h> | 
|  | #include <linux/hdmi.h> | 
|  | #include <linux/i2c.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_graph.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/regulator/consumer.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/v4l2-dv-timings.h> | 
|  | #include <linux/videodev2.h> | 
|  |  | 
|  | #include <media/v4l2-ctrls.h> | 
|  | #include <media/v4l2-device.h> | 
|  | #include <media/v4l2-dv-timings.h> | 
|  | #include <media/v4l2-event.h> | 
|  | #include <media/v4l2-fwnode.h> | 
|  | #include <media/i2c/tda1997x.h> | 
|  |  | 
|  | #include <sound/core.h> | 
|  | #include <sound/pcm.h> | 
|  | #include <sound/pcm_params.h> | 
|  | #include <sound/soc.h> | 
|  |  | 
|  | #include <dt-bindings/media/tda1997x.h> | 
|  |  | 
|  | #include "tda1997x_regs.h" | 
|  |  | 
|  | #define TDA1997X_MBUS_CODES	5 | 
|  |  | 
|  | /* debug level */ | 
|  | static int debug; | 
|  | module_param(debug, int, 0644); | 
|  | MODULE_PARM_DESC(debug, "debug level (0-2)"); | 
|  |  | 
|  | /* Audio formats */ | 
|  | static const char * const audtype_names[] = { | 
|  | "PCM",			/* PCM Samples */ | 
|  | "HBR",			/* High Bit Rate Audio */ | 
|  | "OBA",			/* One-Bit Audio */ | 
|  | "DST"			/* Direct Stream Transfer */ | 
|  | }; | 
|  |  | 
|  | /* Audio output port formats */ | 
|  | enum audfmt_types { | 
|  | AUDFMT_TYPE_DISABLED = 0, | 
|  | AUDFMT_TYPE_I2S, | 
|  | AUDFMT_TYPE_SPDIF, | 
|  | }; | 
|  | static const char * const audfmt_names[] = { | 
|  | "Disabled", | 
|  | "I2S", | 
|  | "SPDIF", | 
|  | }; | 
|  |  | 
|  | /* Video input formats */ | 
|  | static const char * const hdmi_colorspace_names[] = { | 
|  | "RGB", "YUV422", "YUV444", "YUV420", "", "", "", "", | 
|  | }; | 
|  | static const char * const hdmi_colorimetry_names[] = { | 
|  | "", "ITU601", "ITU709", "Extended", | 
|  | }; | 
|  | static const char * const v4l2_quantization_names[] = { | 
|  | "Default", | 
|  | "Full Range (0-255)", | 
|  | "Limited Range (16-235)", | 
|  | }; | 
|  |  | 
|  | /* Video output port formats */ | 
|  | static const char * const vidfmt_names[] = { | 
|  | "RGB444/YUV444",	/* RGB/YUV444 16bit data bus, 8bpp */ | 
|  | "YUV422 semi-planar",	/* YUV422 16bit data base, 8bpp */ | 
|  | "YUV422 CCIR656",	/* BT656 (YUV 8bpp 2 clock per pixel) */ | 
|  | "Invalid", | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Colorspace conversion matrices | 
|  | */ | 
|  | struct color_matrix_coefs { | 
|  | const char *name; | 
|  | /* Input offsets */ | 
|  | s16 offint1; | 
|  | s16 offint2; | 
|  | s16 offint3; | 
|  | /* Coeficients */ | 
|  | s16 p11coef; | 
|  | s16 p12coef; | 
|  | s16 p13coef; | 
|  | s16 p21coef; | 
|  | s16 p22coef; | 
|  | s16 p23coef; | 
|  | s16 p31coef; | 
|  | s16 p32coef; | 
|  | s16 p33coef; | 
|  | /* Output offsets */ | 
|  | s16 offout1; | 
|  | s16 offout2; | 
|  | s16 offout3; | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | ITU709_RGBFULL, | 
|  | ITU601_RGBFULL, | 
|  | RGBLIMITED_RGBFULL, | 
|  | RGBLIMITED_ITU601, | 
|  | RGBLIMITED_ITU709, | 
|  | RGBFULL_ITU601, | 
|  | RGBFULL_ITU709, | 
|  | }; | 
|  |  | 
|  | /* NB: 4096 is 1.0 using fixed point numbers */ | 
|  | static const struct color_matrix_coefs conv_matrix[] = { | 
|  | { | 
|  | "YUV709 -> RGB full", | 
|  | -256, -2048,  -2048, | 
|  | 4769, -2183,   -873, | 
|  | 4769,  7343,      0, | 
|  | 4769,     0,   8652, | 
|  | 0,     0,      0, | 
|  | }, | 
|  | { | 
|  | "YUV601 -> RGB full", | 
|  | -256, -2048,  -2048, | 
|  | 4769, -3330,  -1602, | 
|  | 4769,  6538,      0, | 
|  | 4769,     0,   8264, | 
|  | 256,   256,    256, | 
|  | }, | 
|  | { | 
|  | "RGB limited -> RGB full", | 
|  | -256,  -256,   -256, | 
|  | 0,  4769,      0, | 
|  | 0,     0,   4769, | 
|  | 4769,     0,      0, | 
|  | 0,     0,      0, | 
|  | }, | 
|  | { | 
|  | "RGB limited -> ITU601", | 
|  | -256,  -256,   -256, | 
|  | 2404,  1225,    467, | 
|  | -1754,  2095,   -341, | 
|  | -1388,  -707,   2095, | 
|  | 256,  2048,   2048, | 
|  | }, | 
|  | { | 
|  | "RGB limited -> ITU709", | 
|  | -256,  -256,   -256, | 
|  | 2918,   867,    295, | 
|  | -1894,  2087,   -190, | 
|  | -1607,  -477,   2087, | 
|  | 256,  2048,   2048, | 
|  | }, | 
|  | { | 
|  | "RGB full -> ITU601", | 
|  | 0,     0,      0, | 
|  | 2065,  1052,    401, | 
|  | -1506,  1799,   -293, | 
|  | -1192,  -607,   1799, | 
|  | 256,  2048,   2048, | 
|  | }, | 
|  | { | 
|  | "RGB full -> ITU709", | 
|  | 0,     0,      0, | 
|  | 2506,   745,    253, | 
|  | -1627,  1792,   -163, | 
|  | -1380,  -410,   1792, | 
|  | 256,  2048,   2048, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = { | 
|  | .type = V4L2_DV_BT_656_1120, | 
|  | /* keep this initialization for compatibility with GCC < 4.4.6 */ | 
|  | .reserved = { 0 }, | 
|  |  | 
|  | V4L2_INIT_BT_TIMINGS( | 
|  | 640, 1920,			/* min/max width */ | 
|  | 350, 1200,			/* min/max height */ | 
|  | 13000000, 165000000,		/* min/max pixelclock */ | 
|  | /* standards */ | 
|  | V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | | 
|  | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, | 
|  | /* capabilities */ | 
|  | V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE | | 
|  | V4L2_DV_BT_CAP_REDUCED_BLANKING | | 
|  | V4L2_DV_BT_CAP_CUSTOM | 
|  | ) | 
|  | }; | 
|  |  | 
|  | /* regulator supplies */ | 
|  | static const char * const tda1997x_supply_name[] = { | 
|  | "DOVDD", /* Digital I/O supply */ | 
|  | "DVDD",  /* Digital Core supply */ | 
|  | "AVDD",  /* Analog supply */ | 
|  | }; | 
|  |  | 
|  | #define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name) | 
|  |  | 
|  | enum tda1997x_type { | 
|  | TDA19971, | 
|  | TDA19973, | 
|  | }; | 
|  |  | 
|  | enum tda1997x_hdmi_pads { | 
|  | TDA1997X_PAD_SOURCE, | 
|  | TDA1997X_NUM_PADS, | 
|  | }; | 
|  |  | 
|  | struct tda1997x_chip_info { | 
|  | enum tda1997x_type type; | 
|  | const char *name; | 
|  | }; | 
|  |  | 
|  | struct tda1997x_state { | 
|  | const struct tda1997x_chip_info *info; | 
|  | struct tda1997x_platform_data pdata; | 
|  | struct i2c_client *client; | 
|  | struct i2c_client *client_cec; | 
|  | struct v4l2_subdev sd; | 
|  | struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES]; | 
|  | struct media_pad pads[TDA1997X_NUM_PADS]; | 
|  | struct mutex lock; | 
|  | struct mutex page_lock; | 
|  | char page; | 
|  |  | 
|  | /* detected info from chip */ | 
|  | int chip_revision; | 
|  | char port_30bit; | 
|  | char output_2p5; | 
|  | char tmdsb_clk; | 
|  | char tmdsb_soc; | 
|  |  | 
|  | /* status info */ | 
|  | char hdmi_status; | 
|  | char mptrw_in_progress; | 
|  | char activity_status; | 
|  | char input_detect[2]; | 
|  |  | 
|  | /* video */ | 
|  | struct hdmi_avi_infoframe avi_infoframe; | 
|  | struct v4l2_hdmi_colorimetry colorimetry; | 
|  | u32 rgb_quantization_range; | 
|  | struct v4l2_dv_timings timings; | 
|  | int fps; | 
|  | const struct color_matrix_coefs *conv; | 
|  | u32 mbus_codes[TDA1997X_MBUS_CODES];	/* available modes */ | 
|  | u32 mbus_code;		/* current mode */ | 
|  | u8 vid_fmt; | 
|  |  | 
|  | /* controls */ | 
|  | struct v4l2_ctrl_handler hdl; | 
|  | struct v4l2_ctrl *detect_tx_5v_ctrl; | 
|  | struct v4l2_ctrl *rgb_quantization_range_ctrl; | 
|  |  | 
|  | /* audio */ | 
|  | u8  audio_ch_alloc; | 
|  | int audio_samplerate; | 
|  | int audio_channels; | 
|  | int audio_samplesize; | 
|  | int audio_type; | 
|  | struct mutex audio_lock; | 
|  | struct snd_pcm_substream *audio_stream; | 
|  |  | 
|  | /* EDID */ | 
|  | struct { | 
|  | u8 edid[256]; | 
|  | u32 present; | 
|  | unsigned int blocks; | 
|  | } edid; | 
|  | struct delayed_work delayed_work_enable_hpd; | 
|  | }; | 
|  |  | 
|  | static const struct v4l2_event tda1997x_ev_fmt = { | 
|  | .type = V4L2_EVENT_SOURCE_CHANGE, | 
|  | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, | 
|  | }; | 
|  |  | 
|  | static const struct tda1997x_chip_info tda1997x_chip_info[] = { | 
|  | [TDA19971] = { | 
|  | .type = TDA19971, | 
|  | .name = "tda19971", | 
|  | }, | 
|  | [TDA19973] = { | 
|  | .type = TDA19973, | 
|  | .name = "tda19973", | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd) | 
|  | { | 
|  | return container_of(sd, struct tda1997x_state, sd); | 
|  | } | 
|  |  | 
|  | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | 
|  | { | 
|  | return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd; | 
|  | } | 
|  |  | 
|  | static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int val; | 
|  |  | 
|  | val = i2c_smbus_read_byte_data(state->client_cec, reg); | 
|  | if (val < 0) { | 
|  | v4l_err(state->client, "read reg error: reg=%2x\n", reg); | 
|  | val = -1; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int ret = 0; | 
|  |  | 
|  | ret = i2c_smbus_write_byte_data(state->client_cec, reg, val); | 
|  | if (ret < 0) { | 
|  | v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", | 
|  | reg, val); | 
|  | ret = -1; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * I2C transfer | 
|  | */ | 
|  |  | 
|  | static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int ret; | 
|  |  | 
|  | if (state->page != page) { | 
|  | ret = i2c_smbus_write_byte_data(state->client, | 
|  | REG_CURPAGE_00H, page); | 
|  | if (ret < 0) { | 
|  | v4l_err(state->client, | 
|  | "write reg error:reg=%2x,val=%2x\n", | 
|  | REG_CURPAGE_00H, page); | 
|  | return ret; | 
|  | } | 
|  | state->page = page; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline int io_read(struct v4l2_subdev *sd, u16 reg) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int val; | 
|  |  | 
|  | mutex_lock(&state->page_lock); | 
|  | if (tda1997x_setpage(sd, reg >> 8)) { | 
|  | val = -1; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | val = i2c_smbus_read_byte_data(state->client, reg&0xff); | 
|  | if (val < 0) { | 
|  | v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff); | 
|  | val = -1; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | out: | 
|  | mutex_unlock(&state->page_lock); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static inline long io_read16(struct v4l2_subdev *sd, u16 reg) | 
|  | { | 
|  | int val; | 
|  | long lval = 0; | 
|  |  | 
|  | val = io_read(sd, reg); | 
|  | if (val < 0) | 
|  | return val; | 
|  | lval |= (val << 8); | 
|  | val = io_read(sd, reg + 1); | 
|  | if (val < 0) | 
|  | return val; | 
|  | lval |= val; | 
|  |  | 
|  | return lval; | 
|  | } | 
|  |  | 
|  | static inline long io_read24(struct v4l2_subdev *sd, u16 reg) | 
|  | { | 
|  | int val; | 
|  | long lval = 0; | 
|  |  | 
|  | val = io_read(sd, reg); | 
|  | if (val < 0) | 
|  | return val; | 
|  | lval |= (val << 16); | 
|  | val = io_read(sd, reg + 1); | 
|  | if (val < 0) | 
|  | return val; | 
|  | lval |= (val << 8); | 
|  | val = io_read(sd, reg + 2); | 
|  | if (val < 0) | 
|  | return val; | 
|  | lval |= val; | 
|  |  | 
|  | return lval; | 
|  | } | 
|  |  | 
|  | static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data) | 
|  | { | 
|  | int i; | 
|  | int sz = 0; | 
|  | int val; | 
|  |  | 
|  | for (i = 0; i < len; i++) { | 
|  | val = io_read(sd, reg + i); | 
|  | if (val < 0) | 
|  | break; | 
|  | data[i] = val; | 
|  | sz++; | 
|  | } | 
|  |  | 
|  | return sz; | 
|  | } | 
|  |  | 
|  | static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | s32 ret = 0; | 
|  |  | 
|  | mutex_lock(&state->page_lock); | 
|  | if (tda1997x_setpage(sd, reg >> 8)) { | 
|  | ret = -1; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val); | 
|  | if (ret < 0) { | 
|  | v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", | 
|  | reg&0xff, val); | 
|  | ret = -1; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | out: | 
|  | mutex_unlock(&state->page_lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = io_write(sd, reg, (val >> 8) & 0xff); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | ret = io_write(sd, reg + 1, val & 0xff); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = io_write(sd, reg, (val >> 16) & 0xff); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | ret = io_write(sd, reg + 1, (val >> 8) & 0xff); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | ret = io_write(sd, reg + 2, val & 0xff); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * Hotplug | 
|  | */ | 
|  |  | 
|  | enum hpd_mode { | 
|  | HPD_LOW_BP,	/* HPD low and pulse of at least 100ms */ | 
|  | HPD_LOW_OTHER,	/* HPD low and pulse of at least 100ms */ | 
|  | HPD_HIGH_BP,	/* HIGH */ | 
|  | HPD_HIGH_OTHER, | 
|  | HPD_PULSE,	/* HPD low pulse */ | 
|  | }; | 
|  |  | 
|  | /* manual HPD (Hot Plug Detect) control */ | 
|  | static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode) | 
|  | { | 
|  | u8 hpd_auto, hpd_pwr, hpd_man; | 
|  |  | 
|  | hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL); | 
|  | hpd_pwr = io_read(sd, REG_HPD_POWER); | 
|  | hpd_man = io_read(sd, REG_HPD_MAN_CTRL); | 
|  |  | 
|  | /* mask out unused bits */ | 
|  | hpd_man &= (HPD_MAN_CTRL_HPD_PULSE | | 
|  | HPD_MAN_CTRL_5VEN | | 
|  | HPD_MAN_CTRL_HPD_B | | 
|  | HPD_MAN_CTRL_HPD_A); | 
|  |  | 
|  | switch (mode) { | 
|  | /* HPD low and pulse of at least 100ms */ | 
|  | case HPD_LOW_BP: | 
|  | /* hpd_bp=0 */ | 
|  | hpd_pwr &= ~HPD_POWER_BP_MASK; | 
|  | /* disable HPD_A and HPD_B */ | 
|  | hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); | 
|  | io_write(sd, REG_HPD_POWER, hpd_pwr); | 
|  | io_write(sd, REG_HPD_MAN_CTRL, hpd_man); | 
|  | break; | 
|  | /* HPD high */ | 
|  | case HPD_HIGH_BP: | 
|  | /* hpd_bp=1 */ | 
|  | hpd_pwr &= ~HPD_POWER_BP_MASK; | 
|  | hpd_pwr |= 1 << HPD_POWER_BP_SHIFT; | 
|  | io_write(sd, REG_HPD_POWER, hpd_pwr); | 
|  | break; | 
|  | /* HPD low and pulse of at least 100ms */ | 
|  | case HPD_LOW_OTHER: | 
|  | /* disable HPD_A and HPD_B */ | 
|  | hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); | 
|  | /* hp_other=0 */ | 
|  | hpd_auto &= ~HPD_AUTO_HP_OTHER; | 
|  | io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); | 
|  | io_write(sd, REG_HPD_MAN_CTRL, hpd_man); | 
|  | break; | 
|  | /* HPD high */ | 
|  | case HPD_HIGH_OTHER: | 
|  | hpd_auto |= HPD_AUTO_HP_OTHER; | 
|  | io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); | 
|  | break; | 
|  | /* HPD low pulse */ | 
|  | case HPD_PULSE: | 
|  | /* disable HPD_A and HPD_B */ | 
|  | hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); | 
|  | io_write(sd, REG_HPD_MAN_CTRL, hpd_man); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void tda1997x_delayed_work_enable_hpd(struct work_struct *work) | 
|  | { | 
|  | struct delayed_work *dwork = to_delayed_work(work); | 
|  | struct tda1997x_state *state = container_of(dwork, | 
|  | struct tda1997x_state, | 
|  | delayed_work_enable_hpd); | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  |  | 
|  | v4l2_dbg(2, debug, sd, "%s\n", __func__); | 
|  |  | 
|  | /* Set HPD high */ | 
|  | tda1997x_manual_hpd(sd, HPD_HIGH_OTHER); | 
|  | tda1997x_manual_hpd(sd, HPD_HIGH_BP); | 
|  |  | 
|  | state->edid.present = 1; | 
|  | } | 
|  |  | 
|  | static void tda1997x_disable_edid(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l2_dbg(1, debug, sd, "%s\n", __func__); | 
|  | cancel_delayed_work_sync(&state->delayed_work_enable_hpd); | 
|  |  | 
|  | /* Set HPD low */ | 
|  | tda1997x_manual_hpd(sd, HPD_LOW_BP); | 
|  | } | 
|  |  | 
|  | static void tda1997x_enable_edid(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l2_dbg(1, debug, sd, "%s\n", __func__); | 
|  |  | 
|  | /* Enable hotplug after 100ms */ | 
|  | schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10); | 
|  | } | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * Signal Control | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * configure vid_fmt based on mbus_code | 
|  | */ | 
|  | static int | 
|  | tda1997x_setup_format(struct tda1997x_state *state, u32 code) | 
|  | { | 
|  | v4l_dbg(1, debug, state->client, "%s code=0x%x\n", __func__, code); | 
|  | switch (code) { | 
|  | case MEDIA_BUS_FMT_RGB121212_1X36: | 
|  | case MEDIA_BUS_FMT_RGB888_1X24: | 
|  | case MEDIA_BUS_FMT_YUV12_1X36: | 
|  | case MEDIA_BUS_FMT_YUV8_1X24: | 
|  | state->vid_fmt = OF_FMT_444; | 
|  | break; | 
|  | case MEDIA_BUS_FMT_UYVY12_1X24: | 
|  | case MEDIA_BUS_FMT_UYVY10_1X20: | 
|  | case MEDIA_BUS_FMT_UYVY8_1X16: | 
|  | state->vid_fmt = OF_FMT_422_SMPT; | 
|  | break; | 
|  | case MEDIA_BUS_FMT_UYVY12_2X12: | 
|  | case MEDIA_BUS_FMT_UYVY10_2X10: | 
|  | case MEDIA_BUS_FMT_UYVY8_2X8: | 
|  | state->vid_fmt = OF_FMT_422_CCIR; | 
|  | break; | 
|  | default: | 
|  | v4l_err(state->client, "incompatible format (0x%x)\n", code); | 
|  | return -EINVAL; | 
|  | } | 
|  | v4l_dbg(1, debug, state->client, "%s code=0x%x fmt=%s\n", __func__, | 
|  | code, vidfmt_names[state->vid_fmt]); | 
|  | state->mbus_code = code; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The color conversion matrix will convert between the colorimetry of the | 
|  | * HDMI input to the desired output format RGB|YUV. RGB output is to be | 
|  | * full-range and YUV is to be limited range. | 
|  | * | 
|  | * RGB full-range uses values from 0 to 255 which is recommended on a monitor | 
|  | * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is | 
|  | * typically recommended on a TV. | 
|  | */ | 
|  | static void | 
|  | tda1997x_configure_csc(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct hdmi_avi_infoframe *avi = &state->avi_infoframe; | 
|  | struct v4l2_hdmi_colorimetry *c = &state->colorimetry; | 
|  | /* Blanking code values depend on output colorspace (RGB or YUV) */ | 
|  | struct blanking_codes { | 
|  | s16 code_gy; | 
|  | s16 code_bu; | 
|  | s16 code_rv; | 
|  | }; | 
|  | static const struct blanking_codes rgb_blanking = { 64, 64, 64 }; | 
|  | static const struct blanking_codes yuv_blanking = { 64, 512, 512 }; | 
|  | const struct blanking_codes *blanking_codes = NULL; | 
|  | u8 reg; | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n", | 
|  | hdmi_colorspace_names[avi->colorspace], | 
|  | v4l2_quantization_names[c->quantization], | 
|  | vidfmt_names[state->vid_fmt]); | 
|  | state->conv = NULL; | 
|  | switch (state->vid_fmt) { | 
|  | /* RGB output */ | 
|  | case OF_FMT_444: | 
|  | blanking_codes = &rgb_blanking; | 
|  | if (c->colorspace == V4L2_COLORSPACE_SRGB) { | 
|  | if (c->quantization == V4L2_QUANTIZATION_LIM_RANGE) | 
|  | state->conv = &conv_matrix[RGBLIMITED_RGBFULL]; | 
|  | } else { | 
|  | if (c->colorspace == V4L2_COLORSPACE_REC709) | 
|  | state->conv = &conv_matrix[ITU709_RGBFULL]; | 
|  | else if (c->colorspace == V4L2_COLORSPACE_SMPTE170M) | 
|  | state->conv = &conv_matrix[ITU601_RGBFULL]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | /* YUV output */ | 
|  | case OF_FMT_422_SMPT: /* semi-planar */ | 
|  | case OF_FMT_422_CCIR: /* CCIR656 */ | 
|  | blanking_codes = &yuv_blanking; | 
|  | if ((c->colorspace == V4L2_COLORSPACE_SRGB) && | 
|  | (c->quantization == V4L2_QUANTIZATION_FULL_RANGE)) { | 
|  | if (state->timings.bt.height <= 576) | 
|  | state->conv = &conv_matrix[RGBFULL_ITU601]; | 
|  | else | 
|  | state->conv = &conv_matrix[RGBFULL_ITU709]; | 
|  | } else if ((c->colorspace == V4L2_COLORSPACE_SRGB) && | 
|  | (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)) { | 
|  | if (state->timings.bt.height <= 576) | 
|  | state->conv = &conv_matrix[RGBLIMITED_ITU601]; | 
|  | else | 
|  | state->conv = &conv_matrix[RGBLIMITED_ITU709]; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (state->conv) { | 
|  | v4l_dbg(1, debug, state->client, "%s\n", | 
|  | state->conv->name); | 
|  | /* enable matrix conversion */ | 
|  | reg = io_read(sd, REG_VDP_CTRL); | 
|  | reg &= ~VDP_CTRL_MATRIX_BP; | 
|  | io_write(sd, REG_VDP_CTRL, reg); | 
|  | /* offset inputs */ | 
|  | io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1); | 
|  | io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2); | 
|  | io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3); | 
|  | /* coefficients */ | 
|  | io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef); | 
|  | io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef); | 
|  | /* offset outputs */ | 
|  | io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1); | 
|  | io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2); | 
|  | io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3); | 
|  | } else { | 
|  | /* disable matrix conversion */ | 
|  | reg = io_read(sd, REG_VDP_CTRL); | 
|  | reg |= VDP_CTRL_MATRIX_BP; | 
|  | io_write(sd, REG_VDP_CTRL, reg); | 
|  | } | 
|  |  | 
|  | /* SetBlankingCodes */ | 
|  | if (blanking_codes) { | 
|  | io_write16(sd, REG_BLK_GY, blanking_codes->code_gy); | 
|  | io_write16(sd, REG_BLK_BU, blanking_codes->code_bu); | 
|  | io_write16(sd, REG_BLK_RV, blanking_codes->code_rv); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Configure frame detection window and VHREF timing generator */ | 
|  | static void | 
|  | tda1997x_configure_vhref(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | const struct v4l2_bt_timings *bt = &state->timings.bt; | 
|  | int width, lines; | 
|  | u16 href_start, href_end; | 
|  | u16 vref_f1_start, vref_f2_start; | 
|  | u8 vref_f1_width, vref_f2_width; | 
|  | u8 field_polarity; | 
|  | u16 fieldref_f1_start, fieldref_f2_start; | 
|  | u8 reg; | 
|  |  | 
|  | href_start = bt->hbackporch + bt->hsync + 1; | 
|  | href_end = href_start + bt->width; | 
|  | vref_f1_start = bt->height + bt->vbackporch + bt->vsync + | 
|  | bt->il_vbackporch + bt->il_vsync + | 
|  | bt->il_vfrontporch; | 
|  | vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch; | 
|  | vref_f2_start = 0; | 
|  | vref_f2_width = 0; | 
|  | fieldref_f1_start = 0; | 
|  | fieldref_f2_start = 0; | 
|  | if (bt->interlaced) { | 
|  | vref_f2_start = (bt->height / 2) + | 
|  | (bt->il_vbackporch + bt->il_vsync - 1); | 
|  | vref_f2_width = bt->il_vbackporch + bt->il_vsync + | 
|  | bt->il_vfrontporch; | 
|  | fieldref_f2_start = vref_f2_start + bt->il_vfrontporch + | 
|  | fieldref_f1_start; | 
|  | } | 
|  | field_polarity = 0; | 
|  |  | 
|  | width = V4L2_DV_BT_FRAME_WIDTH(bt); | 
|  | lines = V4L2_DV_BT_FRAME_HEIGHT(bt); | 
|  |  | 
|  | /* | 
|  | * Configure Frame Detection Window: | 
|  | *  horiz area where the VHREF module consider a VSYNC a new frame | 
|  | */ | 
|  | io_write16(sd, REG_FDW_S, 0x2ef); /* start position */ | 
|  | io_write16(sd, REG_FDW_E, 0x141); /* end position */ | 
|  |  | 
|  | /* Set Pixel And Line Counters */ | 
|  | if (state->chip_revision == 0) | 
|  | io_write16(sd, REG_PXCNT_PR, 4); | 
|  | else | 
|  | io_write16(sd, REG_PXCNT_PR, 1); | 
|  | io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF); | 
|  | io_write16(sd, REG_LCNT_PR, 1); | 
|  | io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF); | 
|  |  | 
|  | /* | 
|  | * Configure the VHRef timing generator responsible for rebuilding all | 
|  | * horiz and vert synch and ref signals from its input allowing auto | 
|  | * detection algorithms and forcing predefined modes (480i & 576i) | 
|  | */ | 
|  | reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT; | 
|  | io_write(sd, REG_VHREF_CTRL, reg); | 
|  |  | 
|  | /* | 
|  | * Configure the VHRef timing values. In case the VHREF generator has | 
|  | * been configured in manual mode, this will allow to manually set all | 
|  | * horiz and vert ref values (non-active pixel areas) of the generator | 
|  | * and allows setting the frame reference params. | 
|  | */ | 
|  | /* horizontal reference start/end */ | 
|  | io_write16(sd, REG_HREF_S, href_start & MASK_VHREF); | 
|  | io_write16(sd, REG_HREF_E, href_end & MASK_VHREF); | 
|  | /* vertical reference f1 start/end */ | 
|  | io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF); | 
|  | io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width); | 
|  | /* vertical reference f2 start/end */ | 
|  | io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF); | 
|  | io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width); | 
|  |  | 
|  | /* F1/F2 FREF, field polarity */ | 
|  | reg = fieldref_f1_start & MASK_VHREF; | 
|  | reg |= field_polarity << 8; | 
|  | io_write16(sd, REG_FREF_F1_S, reg); | 
|  | reg = fieldref_f2_start & MASK_VHREF; | 
|  | io_write16(sd, REG_FREF_F2_S, reg); | 
|  | } | 
|  |  | 
|  | /* Configure Video Output port signals */ | 
|  | static int | 
|  | tda1997x_configure_vidout(struct tda1997x_state *state) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | struct tda1997x_platform_data *pdata = &state->pdata; | 
|  | u8 prefilter; | 
|  | u8 reg; | 
|  |  | 
|  | /* Configure pixel clock generator: delay, polarity, rate */ | 
|  | reg = (state->vid_fmt == OF_FMT_422_CCIR) ? | 
|  | PCLK_SEL_X2 : PCLK_SEL_X1; | 
|  | reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT; | 
|  | reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT; | 
|  | io_write(sd, REG_PCLK, reg); | 
|  |  | 
|  | /* Configure pre-filter */ | 
|  | prefilter = 0; /* filters off */ | 
|  | /* YUV422 mode requires conversion */ | 
|  | if ((state->vid_fmt == OF_FMT_422_SMPT) || | 
|  | (state->vid_fmt == OF_FMT_422_CCIR)) { | 
|  | /* 2/7 taps for Rv and Bu */ | 
|  | prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT | | 
|  | FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT; | 
|  | } | 
|  | io_write(sd, REG_FILTERS_CTRL, prefilter); | 
|  |  | 
|  | /* Configure video port */ | 
|  | reg = state->vid_fmt & OF_FMT_MASK; | 
|  | if (state->vid_fmt == OF_FMT_422_CCIR) | 
|  | reg |= (OF_BLK | OF_TRC); | 
|  | reg |= OF_VP_ENABLE; | 
|  | io_write(sd, REG_OF, reg); | 
|  |  | 
|  | /* Configure formatter and conversions */ | 
|  | reg = io_read(sd, REG_VDP_CTRL); | 
|  | /* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */ | 
|  | if (!prefilter) | 
|  | reg |= VDP_CTRL_PREFILTER_BP; | 
|  | else | 
|  | reg &= ~VDP_CTRL_PREFILTER_BP; | 
|  | /* formatter is needed for YUV422 and for trc/blc codes */ | 
|  | if (state->vid_fmt == OF_FMT_444) | 
|  | reg |= VDP_CTRL_FORMATTER_BP; | 
|  | /* formatter and compdel needed for timing/blanking codes */ | 
|  | else | 
|  | reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP); | 
|  | /* activate compdel for small sync delays */ | 
|  | if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) | 
|  | reg &= ~VDP_CTRL_COMPDEL_BP; | 
|  | io_write(sd, REG_VDP_CTRL, reg); | 
|  |  | 
|  | /* Configure DE output signal: delay, polarity, and source */ | 
|  | reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT | | 
|  | pdata->vidout_inv_de << DE_FREF_INV_SHIFT | | 
|  | pdata->vidout_sel_de << DE_FREF_SEL_SHIFT; | 
|  | io_write(sd, REG_DE_FREF, reg); | 
|  |  | 
|  | /* Configure HS/HREF output signal: delay, polarity, and source */ | 
|  | if (state->vid_fmt != OF_FMT_422_CCIR) { | 
|  | reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT | | 
|  | pdata->vidout_inv_hs << HS_HREF_INV_SHIFT | | 
|  | pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT; | 
|  | } else | 
|  | reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT; | 
|  | io_write(sd, REG_HS_HREF, reg); | 
|  |  | 
|  | /* Configure VS/VREF output signal: delay, polarity, and source */ | 
|  | if (state->vid_fmt != OF_FMT_422_CCIR) { | 
|  | reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT | | 
|  | pdata->vidout_inv_vs << VS_VREF_INV_SHIFT | | 
|  | pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT; | 
|  | } else | 
|  | reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT; | 
|  | io_write(sd, REG_VS_VREF, reg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Configure Audio output port signals */ | 
|  | static int | 
|  | tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct tda1997x_platform_data *pdata = &state->pdata; | 
|  | bool sp_used_by_fifo = true; | 
|  | u8 reg; | 
|  |  | 
|  | if (!pdata->audout_format) | 
|  | return 0; | 
|  |  | 
|  | /* channel assignment (CEA-861-D Table 20) */ | 
|  | io_write(sd, REG_AUDIO_PATH, channel_assignment); | 
|  |  | 
|  | /* Audio output configuration */ | 
|  | reg = 0; | 
|  | switch (pdata->audout_format) { | 
|  | case AUDFMT_TYPE_I2S: | 
|  | reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT; | 
|  | break; | 
|  | case AUDFMT_TYPE_SPDIF: | 
|  | reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT; | 
|  | break; | 
|  | } | 
|  | switch (state->audio_type) { | 
|  | case AUDCFG_TYPE_PCM: | 
|  | reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT; | 
|  | break; | 
|  | case AUDCFG_TYPE_OBA: | 
|  | reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT; | 
|  | break; | 
|  | case AUDCFG_TYPE_DST: | 
|  | reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT; | 
|  | sp_used_by_fifo = false; | 
|  | break; | 
|  | case AUDCFG_TYPE_HBR: | 
|  | reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT; | 
|  | if (pdata->audout_layout == 1) { | 
|  | /* demuxed via AP0:AP3 */ | 
|  | reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT; | 
|  | if (pdata->audout_format == AUDFMT_TYPE_SPDIF) | 
|  | sp_used_by_fifo = false; | 
|  | } else { | 
|  | /* straight via AP0 */ | 
|  | reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT; | 
|  | } | 
|  | break; | 
|  | } | 
|  | if (pdata->audout_width == 32) | 
|  | reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT; | 
|  | else | 
|  | reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT; | 
|  |  | 
|  | /* automatic hardware mute */ | 
|  | if (pdata->audio_auto_mute) | 
|  | reg |= AUDCFG_AUTO_MUTE_EN; | 
|  | /* clock polarity */ | 
|  | if (pdata->audout_invert_clk) | 
|  | reg |= AUDCFG_CLK_INVERT; | 
|  | io_write(sd, REG_AUDCFG, reg); | 
|  |  | 
|  | /* audio layout */ | 
|  | reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0; | 
|  | if (!pdata->audout_layoutauto) | 
|  | reg |= AUDIO_LAYOUT_MANUAL; | 
|  | if (sp_used_by_fifo) | 
|  | reg |= AUDIO_LAYOUT_SP_FLAG; | 
|  | io_write(sd, REG_AUDIO_LAYOUT, reg); | 
|  |  | 
|  | /* FIFO Latency value */ | 
|  | io_write(sd, REG_FIFO_LATENCY_VAL, 0x80); | 
|  |  | 
|  | /* Audio output port config */ | 
|  | if (sp_used_by_fifo) { | 
|  | reg = AUDIO_OUT_ENABLE_AP0; | 
|  | if (channel_assignment >= 0x01) | 
|  | reg |= AUDIO_OUT_ENABLE_AP1; | 
|  | if (channel_assignment >= 0x04) | 
|  | reg |= AUDIO_OUT_ENABLE_AP2; | 
|  | if (channel_assignment >= 0x0c) | 
|  | reg |= AUDIO_OUT_ENABLE_AP3; | 
|  | /* specific cases where AP1 is not used */ | 
|  | if ((channel_assignment == 0x04) | 
|  | || (channel_assignment == 0x08) | 
|  | || (channel_assignment == 0x0c) | 
|  | || (channel_assignment == 0x10) | 
|  | || (channel_assignment == 0x14) | 
|  | || (channel_assignment == 0x18) | 
|  | || (channel_assignment == 0x1c)) | 
|  | reg &= ~AUDIO_OUT_ENABLE_AP1; | 
|  | /* specific cases where AP2 is not used */ | 
|  | if ((channel_assignment >= 0x14) | 
|  | && (channel_assignment <= 0x17)) | 
|  | reg &= ~AUDIO_OUT_ENABLE_AP2; | 
|  | } else { | 
|  | reg = AUDIO_OUT_ENABLE_AP3 | | 
|  | AUDIO_OUT_ENABLE_AP2 | | 
|  | AUDIO_OUT_ENABLE_AP1 | | 
|  | AUDIO_OUT_ENABLE_AP0; | 
|  | } | 
|  | if (pdata->audout_format == AUDFMT_TYPE_I2S) | 
|  | reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS); | 
|  | io_write(sd, REG_AUDIO_OUT_ENABLE, reg); | 
|  |  | 
|  | /* reset test mode to normal audio freq auto selection */ | 
|  | io_write(sd, REG_TEST_MODE, 0x00); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Soft Reset of specific hdmi info */ | 
|  | static int | 
|  | tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus) | 
|  | { | 
|  | u8 reg; | 
|  |  | 
|  | /* reset infoframe engine packets */ | 
|  | reg = io_read(sd, REG_HDMI_INFO_RST); | 
|  | io_write(sd, REG_HDMI_INFO_RST, info_rst); | 
|  |  | 
|  | /* if infoframe engine has been reset clear INT_FLG_MODE */ | 
|  | if (reg & RESET_IF) { | 
|  | reg = io_read(sd, REG_INT_FLG_CLR_MODE); | 
|  | io_write(sd, REG_INT_FLG_CLR_MODE, reg); | 
|  | } | 
|  |  | 
|  | /* Disable REFTIM to restart start-up-sequencer (SUS) */ | 
|  | reg = io_read(sd, REG_RATE_CTRL); | 
|  | reg &= ~RATE_REFTIM_ENABLE; | 
|  | if (!reset_sus) | 
|  | reg |= RATE_REFTIM_ENABLE; | 
|  | reg = io_write(sd, REG_RATE_CTRL, reg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | tda1997x_power_mode(struct tda1997x_state *state, bool enable) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg; | 
|  |  | 
|  | if (enable) { | 
|  | /* Automatic control of TMDS */ | 
|  | io_write(sd, REG_PON_OVR_EN, PON_DIS); | 
|  | /* Enable current bias unit */ | 
|  | io_write(sd, REG_CFG1, PON_EN); | 
|  | /* Enable deep color PLL */ | 
|  | io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS); | 
|  | /* Output buffers active */ | 
|  | reg = io_read(sd, REG_OF); | 
|  | reg &= ~OF_VP_ENABLE; | 
|  | io_write(sd, REG_OF, reg); | 
|  | } else { | 
|  | /* Power down EDID mode sequence */ | 
|  | /* Output buffers in HiZ */ | 
|  | reg = io_read(sd, REG_OF); | 
|  | reg |= OF_VP_ENABLE; | 
|  | io_write(sd, REG_OF, reg); | 
|  | /* Disable deep color PLL */ | 
|  | io_write(sd, REG_DEEP_PLL7_BYP, PON_EN); | 
|  | /* Disable current bias unit */ | 
|  | io_write(sd, REG_CFG1, PON_DIS); | 
|  | /* Manual control of TMDS */ | 
|  | io_write(sd, REG_PON_OVR_EN, PON_EN); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool | 
|  | tda1997x_detect_tx_5v(struct v4l2_subdev *sd) | 
|  | { | 
|  | u8 reg = io_read(sd, REG_DETECT_5V); | 
|  |  | 
|  | return ((reg & DETECT_5V_SEL) ? 1 : 0); | 
|  | } | 
|  |  | 
|  | static bool | 
|  | tda1997x_detect_tx_hpd(struct v4l2_subdev *sd) | 
|  | { | 
|  | u8 reg = io_read(sd, REG_DETECT_5V); | 
|  |  | 
|  | return ((reg & DETECT_HPD) ? 1 : 0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | tda1997x_detect_std(struct tda1997x_state *state, | 
|  | struct v4l2_dv_timings *timings) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  |  | 
|  | /* | 
|  | * Read the FMT registers | 
|  | *   REG_V_PER: Period of a frame (or field) in MCLK (27MHz) cycles | 
|  | *   REG_H_PER: Period of a line in MCLK (27MHz) cycles | 
|  | *   REG_HS_WIDTH: Period of horiz sync pulse in MCLK (27MHz) cycles | 
|  | */ | 
|  | u32 vper, vsync_pos; | 
|  | u16 hper, hsync_pos, hsper, interlaced; | 
|  | u16 htot, hact, hfront, hsync, hback; | 
|  | u16 vtot, vact, vfront1, vfront2, vsync, vback1, vback2; | 
|  |  | 
|  | if (!state->input_detect[0] && !state->input_detect[1]) | 
|  | return -ENOLINK; | 
|  |  | 
|  | vper = io_read24(sd, REG_V_PER); | 
|  | hper = io_read16(sd, REG_H_PER); | 
|  | hsper = io_read16(sd, REG_HS_WIDTH); | 
|  | vsync_pos = vper & MASK_VPER_SYNC_POS; | 
|  | hsync_pos = hper & MASK_HPER_SYNC_POS; | 
|  | interlaced = hsper & MASK_HSWIDTH_INTERLACED; | 
|  | vper &= MASK_VPER; | 
|  | hper &= MASK_HPER; | 
|  | hsper &= MASK_HSWIDTH; | 
|  | v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper); | 
|  |  | 
|  | htot = io_read16(sd, REG_FMT_H_TOT); | 
|  | hact = io_read16(sd, REG_FMT_H_ACT); | 
|  | hfront = io_read16(sd, REG_FMT_H_FRONT); | 
|  | hsync = io_read16(sd, REG_FMT_H_SYNC); | 
|  | hback = io_read16(sd, REG_FMT_H_BACK); | 
|  |  | 
|  | vtot = io_read16(sd, REG_FMT_V_TOT); | 
|  | vact = io_read16(sd, REG_FMT_V_ACT); | 
|  | vfront1 = io_read(sd, REG_FMT_V_FRONT_F1); | 
|  | vfront2 = io_read(sd, REG_FMT_V_FRONT_F2); | 
|  | vsync = io_read(sd, REG_FMT_V_SYNC); | 
|  | vback1 = io_read(sd, REG_FMT_V_BACK_F1); | 
|  | vback2 = io_read(sd, REG_FMT_V_BACK_F2); | 
|  |  | 
|  | v4l2_dbg(1, debug, sd, "Geometry: H %u %u %u %u %u Sync%c  V %u %u %u %u %u %u %u Sync%c\n", | 
|  | htot, hact, hfront, hsync, hback, hsync_pos ? '+' : '-', | 
|  | vtot, vact, vfront1, vfront2, vsync, vback1, vback2, vsync_pos ? '+' : '-'); | 
|  |  | 
|  | if (!timings) | 
|  | return 0; | 
|  |  | 
|  | timings->type = V4L2_DV_BT_656_1120; | 
|  | timings->bt.width = hact; | 
|  | timings->bt.hfrontporch = hfront; | 
|  | timings->bt.hsync = hsync; | 
|  | timings->bt.hbackporch = hback; | 
|  | timings->bt.height = vact; | 
|  | timings->bt.vfrontporch = vfront1; | 
|  | timings->bt.vsync = vsync; | 
|  | timings->bt.vbackporch = vback1; | 
|  | timings->bt.interlaced = interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; | 
|  | timings->bt.polarities = vsync_pos ? V4L2_DV_VSYNC_POS_POL : 0; | 
|  | timings->bt.polarities |= hsync_pos ? V4L2_DV_HSYNC_POS_POL : 0; | 
|  |  | 
|  | timings->bt.pixelclock = (u64)htot * vtot * 27000000; | 
|  | if (interlaced) { | 
|  | timings->bt.il_vfrontporch = vfront2; | 
|  | timings->bt.il_vsync = timings->bt.vsync; | 
|  | timings->bt.il_vbackporch = vback2; | 
|  | do_div(timings->bt.pixelclock, vper * 2 /* full frame */); | 
|  | } else { | 
|  | timings->bt.il_vfrontporch = 0; | 
|  | timings->bt.il_vsync = 0; | 
|  | timings->bt.il_vbackporch = 0; | 
|  | do_div(timings->bt.pixelclock, vper); | 
|  | } | 
|  | v4l2_find_dv_timings_cap(timings, &tda1997x_dv_timings_cap, | 
|  | (u32)timings->bt.pixelclock / 500, NULL, NULL); | 
|  | v4l2_print_dv_timings(sd->name, "Detected format: ", timings, false); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* some sort of errata workaround for chip revision 0 (N1) */ | 
|  | static void tda1997x_reset_n1(struct tda1997x_state *state) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg; | 
|  |  | 
|  | /* clear HDMI mode flag in BCAPS */ | 
|  | io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK); | 
|  | io_write(sd, REG_PON_OVR_EN, PON_EN); | 
|  | io_write(sd, REG_PON_CBIAS, PON_EN); | 
|  | io_write(sd, REG_PON_PLL, PON_EN); | 
|  |  | 
|  | reg = io_read(sd, REG_MODE_REC_CFG1); | 
|  | reg &= ~0x06; | 
|  | reg |= 0x02; | 
|  | io_write(sd, REG_MODE_REC_CFG1, reg); | 
|  | io_write(sd, REG_CLK_CFG, CLK_CFG_DIS); | 
|  | io_write(sd, REG_PON_OVR_EN, PON_DIS); | 
|  | reg = io_read(sd, REG_MODE_REC_CFG1); | 
|  | reg &= ~0x06; | 
|  | io_write(sd, REG_MODE_REC_CFG1, reg); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Activity detection must only be notified when stable_clk_x AND active_x | 
|  | * bits are set to 1. If only stable_clk_x bit is set to 1 but not | 
|  | * active_x, it means that the TMDS clock is not in the defined range | 
|  | * and activity detection must not be notified. | 
|  | */ | 
|  | static u8 | 
|  | tda1997x_read_activity_status_regs(struct v4l2_subdev *sd) | 
|  | { | 
|  | u8 reg, status = 0; | 
|  |  | 
|  | /* Read CLK_A_STATUS register */ | 
|  | reg = io_read(sd, REG_CLK_A_STATUS); | 
|  | /* ignore if not active */ | 
|  | if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) | 
|  | reg &= ~MASK_CLK_STABLE; | 
|  | status |= ((reg & MASK_CLK_STABLE) >> 2); | 
|  |  | 
|  | /* Read CLK_B_STATUS register */ | 
|  | reg = io_read(sd, REG_CLK_B_STATUS); | 
|  | /* ignore if not active */ | 
|  | if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) | 
|  | reg &= ~MASK_CLK_STABLE; | 
|  | status |= ((reg & MASK_CLK_STABLE) >> 1); | 
|  |  | 
|  | /* Read the SUS_STATUS register */ | 
|  | reg = io_read(sd, REG_SUS_STATUS); | 
|  |  | 
|  | /* If state = 5 => TMDS is locked */ | 
|  | if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) | 
|  | status |= MASK_SUS_STATE; | 
|  | else | 
|  | status &= ~MASK_SUS_STATE; | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_rgb_quantization_range(struct tda1997x_state *state) | 
|  | { | 
|  | struct v4l2_hdmi_colorimetry *c = &state->colorimetry; | 
|  |  | 
|  | state->colorimetry = v4l2_hdmi_rx_colorimetry(&state->avi_infoframe, | 
|  | NULL, | 
|  | state->timings.bt.height); | 
|  | /* If ycbcr_enc is V4L2_YCBCR_ENC_DEFAULT, we receive RGB */ | 
|  | if (c->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) { | 
|  | switch (state->rgb_quantization_range) { | 
|  | case V4L2_DV_RGB_RANGE_LIMITED: | 
|  | c->quantization = V4L2_QUANTIZATION_FULL_RANGE; | 
|  | break; | 
|  | case V4L2_DV_RGB_RANGE_FULL: | 
|  | c->quantization = V4L2_QUANTIZATION_LIM_RANGE; | 
|  | break; | 
|  | } | 
|  | } | 
|  | v4l_dbg(1, debug, state->client, | 
|  | "colorspace=%d/%d colorimetry=%d range=%s content=%d\n", | 
|  | state->avi_infoframe.colorspace, c->colorspace, | 
|  | state->avi_infoframe.colorimetry, | 
|  | v4l2_quantization_names[c->quantization], | 
|  | state->avi_infoframe.content_type); | 
|  | } | 
|  |  | 
|  | /* parse an infoframe and do some sanity checks on it */ | 
|  | static unsigned int | 
|  | tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | union hdmi_infoframe frame; | 
|  | u8 buffer[40] = { 0 }; | 
|  | u8 reg; | 
|  | int len, err; | 
|  |  | 
|  | /* read data */ | 
|  | len = io_readn(sd, addr, sizeof(buffer), buffer); | 
|  | err = hdmi_infoframe_unpack(&frame, buffer, len); | 
|  | if (err) { | 
|  | v4l_err(state->client, | 
|  | "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", | 
|  | len, addr, buffer[0]); | 
|  | return err; | 
|  | } | 
|  | hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); | 
|  | switch (frame.any.type) { | 
|  | /* Audio InfoFrame: see HDMI spec 8.2.2 */ | 
|  | case HDMI_INFOFRAME_TYPE_AUDIO: | 
|  | /* sample rate */ | 
|  | switch (frame.audio.sample_frequency) { | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: | 
|  | state->audio_samplerate = 32000; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: | 
|  | state->audio_samplerate = 44100; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: | 
|  | state->audio_samplerate = 48000; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: | 
|  | state->audio_samplerate = 88200; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: | 
|  | state->audio_samplerate = 96000; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: | 
|  | state->audio_samplerate = 176400; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: | 
|  | state->audio_samplerate = 192000; | 
|  | break; | 
|  | default: | 
|  | case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* sample size */ | 
|  | switch (frame.audio.sample_size) { | 
|  | case HDMI_AUDIO_SAMPLE_SIZE_16: | 
|  | state->audio_samplesize = 16; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_SIZE_20: | 
|  | state->audio_samplesize = 20; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_SIZE_24: | 
|  | state->audio_samplesize = 24; | 
|  | break; | 
|  | case HDMI_AUDIO_SAMPLE_SIZE_STREAM: | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Channel Count */ | 
|  | state->audio_channels = frame.audio.channels; | 
|  | if (frame.audio.channel_allocation && | 
|  | frame.audio.channel_allocation != state->audio_ch_alloc) { | 
|  | /* use the channel assignment from the infoframe */ | 
|  | state->audio_ch_alloc = frame.audio.channel_allocation; | 
|  | tda1997x_configure_audout(sd, state->audio_ch_alloc); | 
|  | /* reset the audio FIFO */ | 
|  | tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); | 
|  | } | 
|  | break; | 
|  |  | 
|  | /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ | 
|  | case HDMI_INFOFRAME_TYPE_AVI: | 
|  | state->avi_infoframe = frame.avi; | 
|  | set_rgb_quantization_range(state); | 
|  |  | 
|  | /* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */ | 
|  | reg = io_read(sd, REG_PIX_REPEAT); | 
|  | reg &= ~PIX_REPEAT_MASK_UP_SEL; | 
|  | if (frame.avi.colorspace == HDMI_COLORSPACE_YUV422) | 
|  | reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT); | 
|  | io_write(sd, REG_PIX_REPEAT, reg); | 
|  |  | 
|  | /* ConfigurePixelRepeater: repeat n-times each pixel */ | 
|  | reg = io_read(sd, REG_PIX_REPEAT); | 
|  | reg &= ~PIX_REPEAT_MASK_REP; | 
|  | reg |= frame.avi.pixel_repeat; | 
|  | io_write(sd, REG_PIX_REPEAT, reg); | 
|  |  | 
|  | /* configure the receiver with the new colorspace */ | 
|  | tda1997x_configure_csc(sd); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg, source; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_SUS); | 
|  | io_write(sd, REG_INT_FLG_CLR_SUS, source); | 
|  |  | 
|  | if (source & MASK_MPT) { | 
|  | /* reset MTP in use flag if set */ | 
|  | if (state->mptrw_in_progress) | 
|  | state->mptrw_in_progress = 0; | 
|  | } | 
|  |  | 
|  | if (source & MASK_SUS_END) { | 
|  | /* reset audio FIFO */ | 
|  | reg = io_read(sd, REG_HDMI_INFO_RST); | 
|  | reg |= MASK_SR_FIFO_FIFO_CTRL; | 
|  | io_write(sd, REG_HDMI_INFO_RST, reg); | 
|  | reg &= ~MASK_SR_FIFO_FIFO_CTRL; | 
|  | io_write(sd, REG_HDMI_INFO_RST, reg); | 
|  |  | 
|  | /* reset HDMI flags */ | 
|  | state->hdmi_status = 0; | 
|  | } | 
|  |  | 
|  | /* filter FMT interrupt based on SUS state */ | 
|  | reg = io_read(sd, REG_SUS_STATUS); | 
|  | if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) | 
|  | || (source & MASK_MPT)) { | 
|  | source &= ~MASK_FMT; | 
|  | } | 
|  |  | 
|  | if (source & (MASK_FMT | MASK_SUS_END)) { | 
|  | reg = io_read(sd, REG_SUS_STATUS); | 
|  | if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) { | 
|  | v4l_err(state->client, "BAD SUS STATUS\n"); | 
|  | return; | 
|  | } | 
|  | if (debug) | 
|  | tda1997x_detect_std(state, NULL); | 
|  | /* notify user of change in resolution */ | 
|  | v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 source; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_DDC); | 
|  | io_write(sd, REG_INT_FLG_CLR_DDC, source); | 
|  | if (source & MASK_EDID_MTP) { | 
|  | /* reset MTP in use flag if set */ | 
|  | if (state->mptrw_in_progress) | 
|  | state->mptrw_in_progress = 0; | 
|  | } | 
|  |  | 
|  | /* Detection of +5V */ | 
|  | if (source & MASK_DET_5V) { | 
|  | v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, | 
|  | tda1997x_detect_tx_5v(sd)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg, source; | 
|  |  | 
|  | u8 irq_status; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_RATE); | 
|  | io_write(sd, REG_INT_FLG_CLR_RATE, source); | 
|  |  | 
|  | /* read status regs */ | 
|  | irq_status = tda1997x_read_activity_status_regs(sd); | 
|  |  | 
|  | /* | 
|  | * read clock status reg until INT_FLG_CLR_RATE is still 0 | 
|  | * after the read to make sure its the last one | 
|  | */ | 
|  | reg = source; | 
|  | while (reg != 0) { | 
|  | irq_status = tda1997x_read_activity_status_regs(sd); | 
|  | reg = io_read(sd, REG_INT_FLG_CLR_RATE); | 
|  | io_write(sd, REG_INT_FLG_CLR_RATE, reg); | 
|  | source |= reg; | 
|  | } | 
|  |  | 
|  | /* we only pay attention to stability change events */ | 
|  | if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { | 
|  | int input = (source & MASK_RATE_A_ST)?0:1; | 
|  | u8 mask = 1<<input; | 
|  |  | 
|  | /* state change */ | 
|  | if ((irq_status & mask) != (state->activity_status & mask)) { | 
|  | /* activity lost */ | 
|  | if ((irq_status & mask) == 0) { | 
|  | v4l_info(state->client, | 
|  | "HDMI-%c: Digital Activity Lost\n", | 
|  | input+'A'); | 
|  |  | 
|  | /* bypass up/down sampler and pixel repeater */ | 
|  | reg = io_read(sd, REG_PIX_REPEAT); | 
|  | reg &= ~PIX_REPEAT_MASK_UP_SEL; | 
|  | reg &= ~PIX_REPEAT_MASK_REP; | 
|  | io_write(sd, REG_PIX_REPEAT, reg); | 
|  |  | 
|  | if (state->chip_revision == 0) | 
|  | tda1997x_reset_n1(state); | 
|  |  | 
|  | state->input_detect[input] = 0; | 
|  | v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt); | 
|  | } | 
|  |  | 
|  | /* activity detected */ | 
|  | else { | 
|  | v4l_info(state->client, | 
|  | "HDMI-%c: Digital Activity Detected\n", | 
|  | input+'A'); | 
|  | state->input_detect[input] = 1; | 
|  | } | 
|  |  | 
|  | /* hold onto current state */ | 
|  | state->activity_status = (irq_status & mask); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 source; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_INFO); | 
|  | io_write(sd, REG_INT_FLG_CLR_INFO, source); | 
|  |  | 
|  | /* Audio infoframe */ | 
|  | if (source & MASK_AUD_IF) { | 
|  | tda1997x_parse_infoframe(state, AUD_IF); | 
|  | source &= ~MASK_AUD_IF; | 
|  | } | 
|  |  | 
|  | /* Source Product Descriptor infoframe change */ | 
|  | if (source & MASK_SPD_IF) { | 
|  | tda1997x_parse_infoframe(state, SPD_IF); | 
|  | source &= ~MASK_SPD_IF; | 
|  | } | 
|  |  | 
|  | /* Auxiliary Video Information infoframe */ | 
|  | if (source & MASK_AVI_IF) { | 
|  | tda1997x_parse_infoframe(state, AVI_IF); | 
|  | source &= ~MASK_AVI_IF; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg, source; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_AUDIO); | 
|  | io_write(sd, REG_INT_FLG_CLR_AUDIO, source); | 
|  |  | 
|  | /* reset audio FIFO on FIFO pointer error or audio mute */ | 
|  | if (source & MASK_ERROR_FIFO_PT || | 
|  | source & MASK_MUTE_FLG) { | 
|  | /* audio reset audio FIFO */ | 
|  | reg = io_read(sd, REG_SUS_STATUS); | 
|  | if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) { | 
|  | reg = io_read(sd, REG_HDMI_INFO_RST); | 
|  | reg |= MASK_SR_FIFO_FIFO_CTRL; | 
|  | io_write(sd, REG_HDMI_INFO_RST, reg); | 
|  | reg &= ~MASK_SR_FIFO_FIFO_CTRL; | 
|  | io_write(sd, REG_HDMI_INFO_RST, reg); | 
|  | /* reset channel status IT if present */ | 
|  | source &= ~(MASK_CH_STATE); | 
|  | } | 
|  | } | 
|  | if (source & MASK_AUDIO_FREQ_FLG) { | 
|  | static const int freq[] = { | 
|  | 0, 32000, 44100, 48000, 88200, 96000, 176400, 192000 | 
|  | }; | 
|  |  | 
|  | reg = io_read(sd, REG_AUDIO_FREQ); | 
|  | state->audio_samplerate = freq[reg & 7]; | 
|  | v4l_info(state->client, "Audio Frequency Change: %dHz\n", | 
|  | state->audio_samplerate); | 
|  | } | 
|  | if (source & MASK_AUDIO_FLG) { | 
|  | reg = io_read(sd, REG_AUDIO_FLAGS); | 
|  | if (reg & BIT(AUDCFG_TYPE_DST)) | 
|  | state->audio_type = AUDCFG_TYPE_DST; | 
|  | if (reg & BIT(AUDCFG_TYPE_OBA)) | 
|  | state->audio_type = AUDCFG_TYPE_OBA; | 
|  | if (reg & BIT(AUDCFG_TYPE_HBR)) | 
|  | state->audio_type = AUDCFG_TYPE_HBR; | 
|  | if (reg & BIT(AUDCFG_TYPE_PCM)) | 
|  | state->audio_type = AUDCFG_TYPE_PCM; | 
|  | v4l_info(state->client, "Audio Type: %s\n", | 
|  | audtype_names[state->audio_type]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 reg, source; | 
|  |  | 
|  | source = io_read(sd, REG_INT_FLG_CLR_HDCP); | 
|  | io_write(sd, REG_INT_FLG_CLR_HDCP, source); | 
|  |  | 
|  | /* reset MTP in use flag if set */ | 
|  | if (source & MASK_HDCP_MTP) | 
|  | state->mptrw_in_progress = 0; | 
|  | if (source & MASK_STATE_C5) { | 
|  | /* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */ | 
|  | reg = io_read(sd, REG_INT_MASK_TOP); | 
|  | reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); | 
|  | io_write(sd, REG_INT_MASK_TOP, reg); | 
|  | *flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); | 
|  | } | 
|  | } | 
|  |  | 
|  | static irqreturn_t tda1997x_isr_thread(int irq, void *d) | 
|  | { | 
|  | struct tda1997x_state *state = d; | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | u8 flags; | 
|  |  | 
|  | mutex_lock(&state->lock); | 
|  | do { | 
|  | /* read interrupt flags */ | 
|  | flags = io_read(sd, REG_INT_FLG_CLR_TOP); | 
|  | if (flags == 0) | 
|  | break; | 
|  |  | 
|  | /* SUS interrupt source (Input activity events) */ | 
|  | if (flags & INTERRUPT_SUS) | 
|  | tda1997x_irq_sus(state, &flags); | 
|  | /* DDC interrupt source (Display Data Channel) */ | 
|  | else if (flags & INTERRUPT_DDC) | 
|  | tda1997x_irq_ddc(state, &flags); | 
|  | /* RATE interrupt source (Digital Input activity) */ | 
|  | else if (flags & INTERRUPT_RATE) | 
|  | tda1997x_irq_rate(state, &flags); | 
|  | /* Infoframe change interrupt */ | 
|  | else if (flags & INTERRUPT_INFO) | 
|  | tda1997x_irq_info(state, &flags); | 
|  | /* Audio interrupt source: | 
|  | *   freq change, DST,OBA,HBR,ASP flags, mute, FIFO err | 
|  | */ | 
|  | else if (flags & INTERRUPT_AUDIO) | 
|  | tda1997x_irq_audio(state, &flags); | 
|  | /* HDCP interrupt source (content protection) */ | 
|  | if (flags & INTERRUPT_HDCP) | 
|  | tda1997x_irq_hdcp(state, &flags); | 
|  | } while (flags != 0); | 
|  | mutex_unlock(&state->lock); | 
|  |  | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * v4l2_subdev_video_ops | 
|  | */ | 
|  |  | 
|  | static int | 
|  | tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | u32 vper; | 
|  | u16 hper; | 
|  | u16 hsper; | 
|  |  | 
|  | mutex_lock(&state->lock); | 
|  | vper = io_read24(sd, REG_V_PER) & MASK_VPER; | 
|  | hper = io_read16(sd, REG_H_PER) & MASK_HPER; | 
|  | hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; | 
|  | /* | 
|  | * The tda1997x supports A/B inputs but only a single output. | 
|  | * The irq handler monitors for timing changes on both inputs and | 
|  | * sets the input_detect array to 0|1 depending on signal presence. | 
|  | * I believe selection of A vs B is automatic. | 
|  | * | 
|  | * The vper/hper/hsper registers provide the frame period, line period | 
|  | * and horiz sync period (units of MCLK clock cycles (27MHz)) and | 
|  | * testing shows these values to be random if no signal is present | 
|  | * or locked. | 
|  | */ | 
|  | v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n", | 
|  | state->input_detect[0], state->input_detect[1], | 
|  | vper, hper, hsper); | 
|  | if (!state->input_detect[0] && !state->input_detect[1]) | 
|  | *status = V4L2_IN_ST_NO_SIGNAL; | 
|  | else if (!vper || !hper || !hsper) | 
|  | *status = V4L2_IN_ST_NO_SYNC; | 
|  | else | 
|  | *status = 0; | 
|  | mutex_unlock(&state->lock); | 
|  |  | 
|  | return 0; | 
|  | }; | 
|  |  | 
|  | static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, | 
|  | struct v4l2_dv_timings *timings) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s\n", __func__); | 
|  |  | 
|  | if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) | 
|  | return 0; /* no changes */ | 
|  |  | 
|  | if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap, | 
|  | NULL, NULL)) | 
|  | return -ERANGE; | 
|  |  | 
|  | mutex_lock(&state->lock); | 
|  | state->timings = *timings; | 
|  | /* setup frame detection window and VHREF timing generator */ | 
|  | tda1997x_configure_vhref(sd); | 
|  | /* configure colorspace conversion */ | 
|  | tda1997x_configure_csc(sd); | 
|  | mutex_unlock(&state->lock); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, | 
|  | struct v4l2_dv_timings *timings) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s\n", __func__); | 
|  | mutex_lock(&state->lock); | 
|  | *timings = state->timings; | 
|  | mutex_unlock(&state->lock); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, | 
|  | struct v4l2_dv_timings *timings) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int ret; | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s\n", __func__); | 
|  | memset(timings, 0, sizeof(struct v4l2_dv_timings)); | 
|  | mutex_lock(&state->lock); | 
|  | ret = tda1997x_detect_std(state, timings); | 
|  | mutex_unlock(&state->lock); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static const struct v4l2_subdev_video_ops tda1997x_video_ops = { | 
|  | .g_input_status = tda1997x_g_input_status, | 
|  | .s_dv_timings = tda1997x_s_dv_timings, | 
|  | .g_dv_timings = tda1997x_g_dv_timings, | 
|  | .query_dv_timings = tda1997x_query_dv_timings, | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * v4l2_subdev_pad_ops | 
|  | */ | 
|  |  | 
|  | static int tda1997x_init_state(struct v4l2_subdev *sd, | 
|  | struct v4l2_subdev_state *sd_state) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct v4l2_mbus_framefmt *mf; | 
|  |  | 
|  | mf = v4l2_subdev_state_get_format(sd_state, 0); | 
|  | mf->code = state->mbus_codes[0]; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd, | 
|  | struct v4l2_subdev_state *sd_state, | 
|  | struct v4l2_subdev_mbus_code_enum *code) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s %d\n", __func__, code->index); | 
|  | if (code->index >= ARRAY_SIZE(state->mbus_codes)) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (!state->mbus_codes[code->index]) | 
|  | return -EINVAL; | 
|  |  | 
|  | code->code = state->mbus_codes[code->index]; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void tda1997x_fill_format(struct tda1997x_state *state, | 
|  | struct v4l2_mbus_framefmt *format) | 
|  | { | 
|  | const struct v4l2_bt_timings *bt; | 
|  |  | 
|  | memset(format, 0, sizeof(*format)); | 
|  | bt = &state->timings.bt; | 
|  | format->width = bt->width; | 
|  | format->height = bt->height; | 
|  | format->colorspace = state->colorimetry.colorspace; | 
|  | format->field = (bt->interlaced) ? | 
|  | V4L2_FIELD_SEQ_TB : V4L2_FIELD_NONE; | 
|  | } | 
|  |  | 
|  | static int tda1997x_get_format(struct v4l2_subdev *sd, | 
|  | struct v4l2_subdev_state *sd_state, | 
|  | struct v4l2_subdev_format *format) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s pad=%d which=%d\n", | 
|  | __func__, format->pad, format->which); | 
|  |  | 
|  | tda1997x_fill_format(state, &format->format); | 
|  |  | 
|  | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { | 
|  | struct v4l2_mbus_framefmt *fmt; | 
|  |  | 
|  | fmt = v4l2_subdev_state_get_format(sd_state, format->pad); | 
|  | format->format.code = fmt->code; | 
|  | } else | 
|  | format->format.code = state->mbus_code; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_set_format(struct v4l2_subdev *sd, | 
|  | struct v4l2_subdev_state *sd_state, | 
|  | struct v4l2_subdev_format *format) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | u32 code = 0; | 
|  | int i; | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s pad=%d which=%d fmt=0x%x\n", | 
|  | __func__, format->pad, format->which, format->format.code); | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(state->mbus_codes); i++) { | 
|  | if (format->format.code == state->mbus_codes[i]) { | 
|  | code = state->mbus_codes[i]; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!code) | 
|  | code = state->mbus_codes[0]; | 
|  |  | 
|  | tda1997x_fill_format(state, &format->format); | 
|  | format->format.code = code; | 
|  |  | 
|  | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { | 
|  | struct v4l2_mbus_framefmt *fmt; | 
|  |  | 
|  | fmt = v4l2_subdev_state_get_format(sd_state, format->pad); | 
|  | *fmt = format->format; | 
|  | } else { | 
|  | int ret = tda1997x_setup_format(state, format->format.code); | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  | /* mbus_code has changed - re-configure csc/vidout */ | 
|  | tda1997x_configure_csc(sd); | 
|  | tda1997x_configure_vidout(state); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); | 
|  | memset(edid->reserved, 0, sizeof(edid->reserved)); | 
|  |  | 
|  | if (edid->start_block == 0 && edid->blocks == 0) { | 
|  | edid->blocks = state->edid.blocks; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (!state->edid.present) | 
|  | return -ENODATA; | 
|  |  | 
|  | if (edid->start_block >= state->edid.blocks) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (edid->start_block + edid->blocks > state->edid.blocks) | 
|  | edid->blocks = state->edid.blocks - edid->start_block; | 
|  |  | 
|  | memcpy(edid->edid, state->edid.edid + edid->start_block * 128, | 
|  | edid->blocks * 128); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | int i; | 
|  |  | 
|  | v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); | 
|  | memset(edid->reserved, 0, sizeof(edid->reserved)); | 
|  |  | 
|  | if (edid->start_block != 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (edid->blocks == 0) { | 
|  | state->edid.blocks = 0; | 
|  | state->edid.present = 0; | 
|  | tda1997x_disable_edid(sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (edid->blocks > 2) { | 
|  | edid->blocks = 2; | 
|  | return -E2BIG; | 
|  | } | 
|  |  | 
|  | tda1997x_disable_edid(sd); | 
|  |  | 
|  | /* write base EDID */ | 
|  | for (i = 0; i < 128; i++) | 
|  | io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]); | 
|  |  | 
|  | /* write CEA Extension */ | 
|  | for (i = 0; i < 128; i++) | 
|  | io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); | 
|  |  | 
|  | /* store state */ | 
|  | memcpy(state->edid.edid, edid->edid, 256); | 
|  | state->edid.blocks = edid->blocks; | 
|  |  | 
|  | tda1997x_enable_edid(sd); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd, | 
|  | struct v4l2_dv_timings_cap *cap) | 
|  | { | 
|  | *cap = tda1997x_dv_timings_cap; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, | 
|  | struct v4l2_enum_dv_timings *timings) | 
|  | { | 
|  | return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { | 
|  | .enum_mbus_code = tda1997x_enum_mbus_code, | 
|  | .get_fmt = tda1997x_get_format, | 
|  | .set_fmt = tda1997x_set_format, | 
|  | .get_edid = tda1997x_get_edid, | 
|  | .set_edid = tda1997x_set_edid, | 
|  | .dv_timings_cap = tda1997x_get_dv_timings_cap, | 
|  | .enum_dv_timings = tda1997x_enum_dv_timings, | 
|  | }; | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * v4l2_subdev_core_ops | 
|  | */ | 
|  |  | 
|  | static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | union hdmi_infoframe frame; | 
|  | u8 buffer[40] = { 0 }; | 
|  | int len, err; | 
|  |  | 
|  | /* read data */ | 
|  | len = io_readn(sd, addr, sizeof(buffer), buffer); | 
|  | v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); | 
|  | err = hdmi_infoframe_unpack(&frame, buffer, len); | 
|  | if (err) { | 
|  | v4l_err(state->client, | 
|  | "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", | 
|  | len, addr, buffer[0]); | 
|  | return err; | 
|  | } | 
|  | hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_log_status(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct v4l2_dv_timings timings; | 
|  | struct hdmi_avi_infoframe *avi = &state->avi_infoframe; | 
|  |  | 
|  | v4l2_info(sd, "-----Chip status-----\n"); | 
|  | v4l2_info(sd, "Chip: %s N%d\n", state->info->name, | 
|  | state->chip_revision + 1); | 
|  | v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no"); | 
|  |  | 
|  | v4l2_info(sd, "-----Signal status-----\n"); | 
|  | v4l2_info(sd, "Cable detected (+5V power): %s\n", | 
|  | tda1997x_detect_tx_5v(sd) ? "yes" : "no"); | 
|  | v4l2_info(sd, "HPD detected: %s\n", | 
|  | tda1997x_detect_tx_hpd(sd) ? "yes" : "no"); | 
|  |  | 
|  | v4l2_info(sd, "-----Video Timings-----\n"); | 
|  | switch (tda1997x_detect_std(state, &timings)) { | 
|  | case -ENOLINK: | 
|  | v4l2_info(sd, "No video detected\n"); | 
|  | break; | 
|  | case -ERANGE: | 
|  | v4l2_info(sd, "Invalid signal detected\n"); | 
|  | break; | 
|  | } | 
|  | v4l2_print_dv_timings(sd->name, "Configured format: ", | 
|  | &state->timings, true); | 
|  |  | 
|  | v4l2_info(sd, "-----Color space-----\n"); | 
|  | v4l2_info(sd, "Input color space: %s %s %s", | 
|  | hdmi_colorspace_names[avi->colorspace], | 
|  | (avi->colorspace == HDMI_COLORSPACE_RGB) ? "" : | 
|  | hdmi_colorimetry_names[avi->colorimetry], | 
|  | v4l2_quantization_names[state->colorimetry.quantization]); | 
|  | v4l2_info(sd, "Output color space: %s", | 
|  | vidfmt_names[state->vid_fmt]); | 
|  | v4l2_info(sd, "Color space conversion: %s", state->conv ? | 
|  | state->conv->name : "None"); | 
|  |  | 
|  | v4l2_info(sd, "-----Audio-----\n"); | 
|  | if (state->audio_channels) { | 
|  | v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels, | 
|  | state->audio_samplerate); | 
|  | } else { | 
|  | v4l2_info(sd, "audio: none\n"); | 
|  | } | 
|  |  | 
|  | v4l2_info(sd, "-----Infoframes-----\n"); | 
|  | tda1997x_log_infoframe(sd, AUD_IF); | 
|  | tda1997x_log_infoframe(sd, SPD_IF); | 
|  | tda1997x_log_infoframe(sd, AVI_IF); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_subscribe_event(struct v4l2_subdev *sd, | 
|  | struct v4l2_fh *fh, | 
|  | struct v4l2_event_subscription *sub) | 
|  | { | 
|  | switch (sub->type) { | 
|  | case V4L2_EVENT_SOURCE_CHANGE: | 
|  | return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); | 
|  | case V4L2_EVENT_CTRL: | 
|  | return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct v4l2_subdev_core_ops tda1997x_core_ops = { | 
|  | .log_status = tda1997x_log_status, | 
|  | .subscribe_event = tda1997x_subscribe_event, | 
|  | .unsubscribe_event = v4l2_event_subdev_unsubscribe, | 
|  | }; | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * v4l2_subdev_ops | 
|  | */ | 
|  |  | 
|  | static const struct v4l2_subdev_ops tda1997x_subdev_ops = { | 
|  | .core = &tda1997x_core_ops, | 
|  | .video = &tda1997x_video_ops, | 
|  | .pad = &tda1997x_pad_ops, | 
|  | }; | 
|  |  | 
|  | static const struct v4l2_subdev_internal_ops tda1997x_internal_ops = { | 
|  | .init_state = tda1997x_init_state, | 
|  | }; | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * v4l2_controls | 
|  | */ | 
|  |  | 
|  | static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl) | 
|  | { | 
|  | struct v4l2_subdev *sd = to_sd(ctrl); | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | switch (ctrl->id) { | 
|  | /* allow overriding the default RGB quantization range */ | 
|  | case V4L2_CID_DV_RX_RGB_RANGE: | 
|  | state->rgb_quantization_range = ctrl->val; | 
|  | set_rgb_quantization_range(state); | 
|  | tda1997x_configure_csc(sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return -EINVAL; | 
|  | }; | 
|  |  | 
|  | static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | 
|  | { | 
|  | struct v4l2_subdev *sd = to_sd(ctrl); | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  |  | 
|  | if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) { | 
|  | ctrl->val = state->avi_infoframe.content_type; | 
|  | return 0; | 
|  | } | 
|  | return -EINVAL; | 
|  | }; | 
|  |  | 
|  | static const struct v4l2_ctrl_ops tda1997x_ctrl_ops = { | 
|  | .s_ctrl = tda1997x_s_ctrl, | 
|  | .g_volatile_ctrl = tda1997x_g_volatile_ctrl, | 
|  | }; | 
|  |  | 
|  | static int tda1997x_core_init(struct v4l2_subdev *sd) | 
|  | { | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct tda1997x_platform_data *pdata = &state->pdata; | 
|  | u8 reg; | 
|  | int i; | 
|  |  | 
|  | /* disable HPD */ | 
|  | io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL); | 
|  | if (state->chip_revision == 0) { | 
|  | io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); | 
|  | io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); | 
|  | } | 
|  |  | 
|  | /* reset infoframe at end of start-up-sequencer */ | 
|  | io_write(sd, REG_SUS_SET_RGB2, 0x06); | 
|  | io_write(sd, REG_SUS_SET_RGB3, 0x06); | 
|  |  | 
|  | /* Enable TMDS pull-ups */ | 
|  | io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT | | 
|  | RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A); | 
|  |  | 
|  | /* enable sync measurement timing */ | 
|  | tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04); | 
|  | /* adjust CEC clock divider */ | 
|  | tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03); | 
|  | tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); | 
|  | io_write(sd, REG_TIMER_D, 0x54); | 
|  | /* enable power switch */ | 
|  | reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff); | 
|  | reg |= 0x20; | 
|  | tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg); | 
|  | mdelay(50); | 
|  |  | 
|  | /* read the chip version */ | 
|  | reg = io_read(sd, REG_VERSION); | 
|  | /* get the chip configuration */ | 
|  | reg = io_read(sd, REG_CMTP_REG10); | 
|  |  | 
|  | /* enable interrupts we care about */ | 
|  | io_write(sd, REG_INT_MASK_TOP, | 
|  | INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO | | 
|  | INTERRUPT_RATE | INTERRUPT_SUS); | 
|  | /* config_mtp,fmt,sus_end,sus_st */ | 
|  | io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END); | 
|  | /* rate stability change for inputs A/B */ | 
|  | io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST); | 
|  | /* aud,spd,avi*/ | 
|  | io_write(sd, REG_INT_MASK_INFO, | 
|  | MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF); | 
|  | /* audio_freq,audio_flg,mute_flg,fifo_err */ | 
|  | io_write(sd, REG_INT_MASK_AUDIO, | 
|  | MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG | | 
|  | MASK_ERROR_FIFO_PT); | 
|  | /* HDCP C5 state reached */ | 
|  | io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5); | 
|  | /* 5V detect and HDP pulse end */ | 
|  | io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V); | 
|  | /* don't care about AFE/MODE */ | 
|  | io_write(sd, REG_INT_MASK_AFE, 0); | 
|  | io_write(sd, REG_INT_MASK_MODE, 0); | 
|  |  | 
|  | /* clear all interrupts */ | 
|  | io_write(sd, REG_INT_FLG_CLR_TOP, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_SUS, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_DDC, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_RATE, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_MODE, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_INFO, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff); | 
|  | io_write(sd, REG_INT_FLG_CLR_AFE, 0xff); | 
|  |  | 
|  | /* init TMDS equalizer */ | 
|  | if (state->chip_revision == 0) | 
|  | io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); | 
|  | io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE); | 
|  | io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE); | 
|  | if (state->chip_revision == 0) | 
|  | io_write(sd, REG_WDL_CFG, WDL_CFG_VAL); | 
|  | /* DC filter */ | 
|  | io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); | 
|  | /* disable test pattern */ | 
|  | io_write(sd, REG_SVC_MODE, 0x00); | 
|  | /* update HDMI INFO CTRL */ | 
|  | io_write(sd, REG_INFO_CTRL, 0xff); | 
|  | /* write HDMI INFO EXCEED value */ | 
|  | io_write(sd, REG_INFO_EXCEED, 3); | 
|  |  | 
|  | if (state->chip_revision == 0) | 
|  | tda1997x_reset_n1(state); | 
|  |  | 
|  | /* | 
|  | * No HDCP acknowledge when HDCP is disabled | 
|  | * and reset SUS to force format detection | 
|  | */ | 
|  | tda1997x_hdmi_info_reset(sd, NACK_HDCP, true); | 
|  |  | 
|  | /* Set HPD low */ | 
|  | tda1997x_manual_hpd(sd, HPD_LOW_BP); | 
|  |  | 
|  | /* Configure receiver capabilities */ | 
|  | io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); | 
|  |  | 
|  | /* Configure HDMI: Auto HDCP mode, packet controlled mute */ | 
|  | reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT; | 
|  | reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT; | 
|  | io_write(sd, REG_HDMI_CTRL, reg); | 
|  |  | 
|  | /* reset start-up-sequencer to force format detection */ | 
|  | tda1997x_hdmi_info_reset(sd, 0, true); | 
|  |  | 
|  | /* disable matrix conversion */ | 
|  | reg = io_read(sd, REG_VDP_CTRL); | 
|  | reg |= VDP_CTRL_MATRIX_BP; | 
|  | io_write(sd, REG_VDP_CTRL, reg); | 
|  |  | 
|  | /* set video output mode */ | 
|  | tda1997x_configure_vidout(state); | 
|  |  | 
|  | /* configure video output port */ | 
|  | for (i = 0; i < 9; i++) { | 
|  | v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i, | 
|  | pdata->vidout_port_cfg[i]); | 
|  | io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]); | 
|  | } | 
|  |  | 
|  | /* configure audio output port */ | 
|  | tda1997x_configure_audout(sd, 0); | 
|  |  | 
|  | /* configure audio clock freq */ | 
|  | switch (pdata->audout_mclk_fs) { | 
|  | case 512: | 
|  | reg = AUDIO_CLOCK_SEL_512FS; | 
|  | break; | 
|  | case 256: | 
|  | reg = AUDIO_CLOCK_SEL_256FS; | 
|  | break; | 
|  | case 128: | 
|  | reg = AUDIO_CLOCK_SEL_128FS; | 
|  | break; | 
|  | case 64: | 
|  | reg = AUDIO_CLOCK_SEL_64FS; | 
|  | break; | 
|  | case 32: | 
|  | reg = AUDIO_CLOCK_SEL_32FS; | 
|  | break; | 
|  | default: | 
|  | reg = AUDIO_CLOCK_SEL_16FS; | 
|  | break; | 
|  | } | 
|  | io_write(sd, REG_AUDIO_CLOCK, reg); | 
|  |  | 
|  | /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ | 
|  | tda1997x_hdmi_info_reset(sd, RESET_AI, false); | 
|  | /* reset infoframe */ | 
|  | tda1997x_hdmi_info_reset(sd, RESET_IF, false); | 
|  | /* reset audio infoframes */ | 
|  | tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); | 
|  | /* reset gamut */ | 
|  | tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false); | 
|  |  | 
|  | /* get initial HDMI status */ | 
|  | state->hdmi_status = io_read(sd, REG_HDMI_FLAGS); | 
|  |  | 
|  | io_write(sd, REG_EDID_ENABLE, EDID_ENABLE_A_EN | EDID_ENABLE_B_EN); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_set_power(struct tda1997x_state *state, bool on) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | if (on) { | 
|  | ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES, | 
|  | state->supplies); | 
|  | msleep(300); | 
|  | } else { | 
|  | ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, | 
|  | state->supplies); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static const struct i2c_device_id tda1997x_i2c_id[] = { | 
|  | {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, | 
|  | {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); | 
|  |  | 
|  | static const struct of_device_id tda1997x_of_id[] __maybe_unused = { | 
|  | { .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] }, | 
|  | { .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, tda1997x_of_id); | 
|  |  | 
|  | static int tda1997x_parse_dt(struct tda1997x_state *state) | 
|  | { | 
|  | struct tda1997x_platform_data *pdata = &state->pdata; | 
|  | struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; | 
|  | struct device_node *ep; | 
|  | struct device_node *np; | 
|  | unsigned int flags; | 
|  | const char *str; | 
|  | int ret; | 
|  | u32 v; | 
|  |  | 
|  | /* | 
|  | * setup default values: | 
|  | * - HREF: active high from start to end of row | 
|  | * - VS: Vertical Sync active high at beginning of frame | 
|  | * - DE: Active high when data valid | 
|  | * - A_CLK: 128*Fs | 
|  | */ | 
|  | pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF; | 
|  | pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI; | 
|  | pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF; | 
|  |  | 
|  | np = state->client->dev.of_node; | 
|  | ep = of_graph_get_endpoint_by_regs(np, 0, -1); | 
|  | if (!ep) | 
|  | return -EINVAL; | 
|  |  | 
|  | ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); | 
|  | if (ret) { | 
|  | of_node_put(ep); | 
|  | return ret; | 
|  | } | 
|  | of_node_put(ep); | 
|  | pdata->vidout_bus_type = bus_cfg.bus_type; | 
|  |  | 
|  | /* polarity of HS/VS/DE */ | 
|  | flags = bus_cfg.bus.parallel.flags; | 
|  | if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | 
|  | pdata->vidout_inv_hs = 1; | 
|  | if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | 
|  | pdata->vidout_inv_vs = 1; | 
|  | if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) | 
|  | pdata->vidout_inv_de = 1; | 
|  | pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width; | 
|  |  | 
|  | /* video output port config */ | 
|  | ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg"); | 
|  | if (ret > 0) { | 
|  | u32 reg, val, i; | 
|  |  | 
|  | for (i = 0; i < ret / 2 && i < 9; i++) { | 
|  | of_property_read_u32_index(np, "nxp,vidout-portcfg", | 
|  | i * 2, ®); | 
|  | of_property_read_u32_index(np, "nxp,vidout-portcfg", | 
|  | i * 2 + 1, &val); | 
|  | if (reg < 9) | 
|  | pdata->vidout_port_cfg[reg] = val; | 
|  | } | 
|  | } else { | 
|  | v4l_err(state->client, "nxp,vidout-portcfg missing\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* default to channel layout dictated by packet header */ | 
|  | pdata->audout_layoutauto = true; | 
|  |  | 
|  | pdata->audout_format = AUDFMT_TYPE_DISABLED; | 
|  | if (!of_property_read_string(np, "nxp,audout-format", &str)) { | 
|  | if (strcmp(str, "i2s") == 0) | 
|  | pdata->audout_format = AUDFMT_TYPE_I2S; | 
|  | else if (strcmp(str, "spdif") == 0) | 
|  | pdata->audout_format = AUDFMT_TYPE_SPDIF; | 
|  | else { | 
|  | v4l_err(state->client, "nxp,audout-format invalid\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | if (!of_property_read_u32(np, "nxp,audout-layout", &v)) { | 
|  | switch (v) { | 
|  | case 0: | 
|  | case 1: | 
|  | break; | 
|  | default: | 
|  | v4l_err(state->client, | 
|  | "nxp,audout-layout invalid\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | pdata->audout_layout = v; | 
|  | } | 
|  | if (!of_property_read_u32(np, "nxp,audout-width", &v)) { | 
|  | switch (v) { | 
|  | case 16: | 
|  | case 32: | 
|  | break; | 
|  | default: | 
|  | v4l_err(state->client, | 
|  | "nxp,audout-width invalid\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | pdata->audout_width = v; | 
|  | } | 
|  | if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) { | 
|  | switch (v) { | 
|  | case 512: | 
|  | case 256: | 
|  | case 128: | 
|  | case 64: | 
|  | case 32: | 
|  | case 16: | 
|  | break; | 
|  | default: | 
|  | v4l_err(state->client, | 
|  | "nxp,audout-mclk-fs invalid\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | pdata->audout_mclk_fs = v; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tda1997x_get_regulators(struct tda1997x_state *state) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++) | 
|  | state->supplies[i].supply = tda1997x_supply_name[i]; | 
|  |  | 
|  | return devm_regulator_bulk_get(&state->client->dev, | 
|  | TDA1997X_NUM_SUPPLIES, | 
|  | state->supplies); | 
|  | } | 
|  |  | 
|  | static int tda1997x_identify_module(struct tda1997x_state *state) | 
|  | { | 
|  | struct v4l2_subdev *sd = &state->sd; | 
|  | enum tda1997x_type type; | 
|  | u8 reg; | 
|  |  | 
|  | /* Read chip configuration*/ | 
|  | reg = io_read(sd, REG_CMTP_REG10); | 
|  | state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ | 
|  | state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ | 
|  | state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ | 
|  | state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ | 
|  | switch ((reg >> 4) & 0x03) { | 
|  | case 0x00: | 
|  | type = TDA19971; | 
|  | break; | 
|  | case 0x02: | 
|  | case 0x03: | 
|  | type = TDA19973; | 
|  | break; | 
|  | default: | 
|  | dev_err(&state->client->dev, "unsupported chip ID\n"); | 
|  | return -EIO; | 
|  | } | 
|  | if (state->info->type != type) { | 
|  | dev_err(&state->client->dev, "chip id mismatch\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | /* read chip revision */ | 
|  | state->chip_revision = io_read(sd, REG_CMTP_REG11); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct media_entity_operations tda1997x_media_ops = { | 
|  | .link_validate = v4l2_subdev_link_validate, | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * HDMI Audio Codec | 
|  | */ | 
|  |  | 
|  | /* refine sample-rate based on HDMI source */ | 
|  | static int tda1997x_pcm_startup(struct snd_pcm_substream *substream, | 
|  | struct snd_soc_dai *dai) | 
|  | { | 
|  | struct v4l2_subdev *sd = snd_soc_dai_get_drvdata(dai); | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct snd_soc_component *component = dai->component; | 
|  | struct snd_pcm_runtime *rtd = substream->runtime; | 
|  | int rate, err; | 
|  |  | 
|  | rate = state->audio_samplerate; | 
|  | err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE, | 
|  | rate, rate); | 
|  | if (err < 0) { | 
|  | dev_err(component->dev, "failed to constrain samplerate to %dHz\n", | 
|  | rate); | 
|  | return err; | 
|  | } | 
|  | dev_info(component->dev, "set samplerate constraint to %dHz\n", rate); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct snd_soc_dai_ops tda1997x_dai_ops = { | 
|  | .startup = tda1997x_pcm_startup, | 
|  | }; | 
|  |  | 
|  | static struct snd_soc_dai_driver tda1997x_audio_dai = { | 
|  | .name = "tda1997x", | 
|  | .capture = { | 
|  | .stream_name = "Capture", | 
|  | .channels_min = 2, | 
|  | .channels_max = 8, | 
|  | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | 
|  | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | | 
|  | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | | 
|  | SNDRV_PCM_RATE_192000, | 
|  | }, | 
|  | .ops = &tda1997x_dai_ops, | 
|  | }; | 
|  |  | 
|  | static int tda1997x_codec_probe(struct snd_soc_component *component) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void tda1997x_codec_remove(struct snd_soc_component *component) | 
|  | { | 
|  | } | 
|  |  | 
|  | static struct snd_soc_component_driver tda1997x_codec_driver = { | 
|  | .probe			= tda1997x_codec_probe, | 
|  | .remove			= tda1997x_codec_remove, | 
|  | .idle_bias_on		= 1, | 
|  | .use_pmdown_time	= 1, | 
|  | .endianness		= 1, | 
|  | }; | 
|  |  | 
|  | static int tda1997x_probe(struct i2c_client *client) | 
|  | { | 
|  | const struct i2c_device_id *id = i2c_client_get_device_id(client); | 
|  | struct tda1997x_state *state; | 
|  | struct tda1997x_platform_data *pdata; | 
|  | struct v4l2_subdev *sd; | 
|  | struct v4l2_ctrl_handler *hdl; | 
|  | struct v4l2_ctrl *ctrl; | 
|  | static const struct v4l2_dv_timings cea1920x1080 = | 
|  | V4L2_DV_BT_CEA_1920X1080P60; | 
|  | u32 *mbus_codes; | 
|  | int i, ret; | 
|  |  | 
|  | /* Check if the adapter supports the needed features */ | 
|  | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | 
|  | return -EIO; | 
|  |  | 
|  | state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL); | 
|  | if (!state) | 
|  | return -ENOMEM; | 
|  |  | 
|  | state->client = client; | 
|  | pdata = &state->pdata; | 
|  | if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { | 
|  | const struct of_device_id *oid; | 
|  |  | 
|  | oid = of_match_node(tda1997x_of_id, client->dev.of_node); | 
|  | state->info = oid->data; | 
|  |  | 
|  | ret = tda1997x_parse_dt(state); | 
|  | if (ret < 0) { | 
|  | v4l_err(client, "DT parsing error\n"); | 
|  | goto err_free_state; | 
|  | } | 
|  | } else if (client->dev.platform_data) { | 
|  | struct tda1997x_platform_data *pdata = | 
|  | client->dev.platform_data; | 
|  | state->info = | 
|  | (const struct tda1997x_chip_info *)id->driver_data; | 
|  | state->pdata = *pdata; | 
|  | } else { | 
|  | v4l_err(client, "No platform data\n"); | 
|  | ret = -ENODEV; | 
|  | goto err_free_state; | 
|  | } | 
|  |  | 
|  | ret = tda1997x_get_regulators(state); | 
|  | if (ret) | 
|  | goto err_free_state; | 
|  |  | 
|  | ret = tda1997x_set_power(state, 1); | 
|  | if (ret) | 
|  | goto err_free_state; | 
|  |  | 
|  | mutex_init(&state->page_lock); | 
|  | mutex_init(&state->lock); | 
|  | state->page = 0xff; | 
|  |  | 
|  | INIT_DELAYED_WORK(&state->delayed_work_enable_hpd, | 
|  | tda1997x_delayed_work_enable_hpd); | 
|  |  | 
|  | /* set video format based on chip and bus width */ | 
|  | ret = tda1997x_identify_module(state); | 
|  | if (ret) | 
|  | goto err_free_mutex; | 
|  |  | 
|  | /* initialize subdev */ | 
|  | sd = &state->sd; | 
|  | v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); | 
|  | sd->internal_ops = &tda1997x_internal_ops; | 
|  | snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", | 
|  | id->name, i2c_adapter_id(client->adapter), | 
|  | client->addr); | 
|  | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; | 
|  | sd->entity.function = MEDIA_ENT_F_DV_DECODER; | 
|  | sd->entity.ops = &tda1997x_media_ops; | 
|  |  | 
|  | /* set allowed mbus modes based on chip, bus-type, and bus-width */ | 
|  | i = 0; | 
|  | mbus_codes = state->mbus_codes; | 
|  | switch (state->info->type) { | 
|  | case TDA19973: | 
|  | switch (pdata->vidout_bus_type) { | 
|  | case V4L2_MBUS_PARALLEL: | 
|  | switch (pdata->vidout_bus_width) { | 
|  | case 36: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36; | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36; | 
|  | fallthrough; | 
|  | case 24: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case V4L2_MBUS_BT656: | 
|  | switch (pdata->vidout_bus_width) { | 
|  | case 36: | 
|  | case 24: | 
|  | case 12: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case TDA19971: | 
|  | switch (pdata->vidout_bus_type) { | 
|  | case V4L2_MBUS_PARALLEL: | 
|  | switch (pdata->vidout_bus_width) { | 
|  | case 24: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24; | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24; | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; | 
|  | fallthrough; | 
|  | case 20: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20; | 
|  | fallthrough; | 
|  | case 16: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case V4L2_MBUS_BT656: | 
|  | switch (pdata->vidout_bus_width) { | 
|  | case 24: | 
|  | case 20: | 
|  | case 16: | 
|  | case 12: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; | 
|  | fallthrough; | 
|  | case 10: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; | 
|  | fallthrough; | 
|  | case 8: | 
|  | mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | if (WARN_ON(i > ARRAY_SIZE(state->mbus_codes))) { | 
|  | ret = -EINVAL; | 
|  | goto err_free_mutex; | 
|  | } | 
|  |  | 
|  | /* default format */ | 
|  | tda1997x_setup_format(state, state->mbus_codes[0]); | 
|  | state->timings = cea1920x1080; | 
|  |  | 
|  | /* | 
|  | * default to SRGB full range quantization | 
|  | * (in case we don't get an infoframe such as DVI signal | 
|  | */ | 
|  | state->colorimetry.colorspace = V4L2_COLORSPACE_SRGB; | 
|  | state->colorimetry.quantization = V4L2_QUANTIZATION_FULL_RANGE; | 
|  |  | 
|  | /* disable/reset HDCP to get correct I2C access to Rx HDMI */ | 
|  | io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); | 
|  |  | 
|  | /* | 
|  | * if N2 version, reset compdel_bp as it may generate some small pixel | 
|  | * shifts in case of embedded sync/or delay lower than 4 | 
|  | */ | 
|  | if (state->chip_revision != 0) { | 
|  | io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00); | 
|  | io_write(sd, REG_VDP_CTRL, 0x1f); | 
|  | } | 
|  |  | 
|  | v4l_info(client, "NXP %s N%d detected\n", state->info->name, | 
|  | state->chip_revision + 1); | 
|  | v4l_info(client, "video: %dbit %s %d formats available\n", | 
|  | pdata->vidout_bus_width, | 
|  | (pdata->vidout_bus_type == V4L2_MBUS_PARALLEL) ? | 
|  | "parallel" : "BT656", | 
|  | i); | 
|  | if (pdata->audout_format) { | 
|  | v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n", | 
|  | pdata->audout_layout ? 2 : 8, | 
|  | audfmt_names[pdata->audout_format], | 
|  | pdata->audout_layout, | 
|  | pdata->audout_mclk_fs); | 
|  | } | 
|  |  | 
|  | ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03); | 
|  | state->client_cec = devm_i2c_new_dummy_device(&client->dev, | 
|  | client->adapter, ret); | 
|  | if (IS_ERR(state->client_cec)) { | 
|  | ret = PTR_ERR(state->client_cec); | 
|  | goto err_free_mutex; | 
|  | } | 
|  |  | 
|  | v4l_info(client, "CEC slave address 0x%02x\n", ret); | 
|  |  | 
|  | ret = tda1997x_core_init(sd); | 
|  | if (ret) | 
|  | goto err_free_mutex; | 
|  |  | 
|  | /* control handlers */ | 
|  | hdl = &state->hdl; | 
|  | v4l2_ctrl_handler_init(hdl, 3); | 
|  | ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops, | 
|  | V4L2_CID_DV_RX_IT_CONTENT_TYPE, | 
|  | V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, | 
|  | V4L2_DV_IT_CONTENT_TYPE_NO_ITC); | 
|  | if (ctrl) | 
|  | ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; | 
|  | /* custom controls */ | 
|  | state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, | 
|  | V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); | 
|  | state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, | 
|  | &tda1997x_ctrl_ops, | 
|  | V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, | 
|  | V4L2_DV_RGB_RANGE_AUTO); | 
|  | state->sd.ctrl_handler = hdl; | 
|  | if (hdl->error) { | 
|  | ret = hdl->error; | 
|  | goto err_free_handler; | 
|  | } | 
|  | v4l2_ctrl_handler_setup(hdl); | 
|  |  | 
|  | /* initialize source pads */ | 
|  | state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | 
|  | ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS, | 
|  | state->pads); | 
|  | if (ret) { | 
|  | v4l_err(client, "failed entity_init: %d", ret); | 
|  | goto err_free_handler; | 
|  | } | 
|  |  | 
|  | ret = v4l2_async_register_subdev(sd); | 
|  | if (ret) | 
|  | goto err_free_media; | 
|  |  | 
|  | /* register audio DAI */ | 
|  | if (pdata->audout_format) { | 
|  | u64 formats; | 
|  |  | 
|  | if (pdata->audout_width == 32) | 
|  | formats = SNDRV_PCM_FMTBIT_S32_LE; | 
|  | else | 
|  | formats = SNDRV_PCM_FMTBIT_S16_LE; | 
|  | tda1997x_audio_dai.capture.formats = formats; | 
|  | ret = devm_snd_soc_register_component(&state->client->dev, | 
|  | &tda1997x_codec_driver, | 
|  | &tda1997x_audio_dai, 1); | 
|  | if (ret) { | 
|  | dev_err(&client->dev, "register audio codec failed\n"); | 
|  | goto err_free_media; | 
|  | } | 
|  | v4l_info(state->client, "registered audio codec\n"); | 
|  | } | 
|  |  | 
|  | /* request irq */ | 
|  | ret = devm_request_threaded_irq(&client->dev, client->irq, | 
|  | NULL, tda1997x_isr_thread, | 
|  | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | 
|  | KBUILD_MODNAME, state); | 
|  | if (ret) { | 
|  | v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret); | 
|  | goto err_free_media; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_free_media: | 
|  | media_entity_cleanup(&sd->entity); | 
|  | err_free_handler: | 
|  | v4l2_ctrl_handler_free(&state->hdl); | 
|  | err_free_mutex: | 
|  | cancel_delayed_work(&state->delayed_work_enable_hpd); | 
|  | mutex_destroy(&state->page_lock); | 
|  | mutex_destroy(&state->lock); | 
|  | tda1997x_set_power(state, 0); | 
|  | err_free_state: | 
|  | kfree(state); | 
|  | dev_err(&client->dev, "%s failed: %d\n", __func__, ret); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void tda1997x_remove(struct i2c_client *client) | 
|  | { | 
|  | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 
|  | struct tda1997x_state *state = to_state(sd); | 
|  | struct tda1997x_platform_data *pdata = &state->pdata; | 
|  |  | 
|  | if (pdata->audout_format) { | 
|  | mutex_destroy(&state->audio_lock); | 
|  | } | 
|  |  | 
|  | disable_irq(state->client->irq); | 
|  | tda1997x_power_mode(state, 0); | 
|  |  | 
|  | v4l2_async_unregister_subdev(sd); | 
|  | media_entity_cleanup(&sd->entity); | 
|  | v4l2_ctrl_handler_free(&state->hdl); | 
|  | regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); | 
|  | cancel_delayed_work_sync(&state->delayed_work_enable_hpd); | 
|  | mutex_destroy(&state->page_lock); | 
|  | mutex_destroy(&state->lock); | 
|  |  | 
|  | kfree(state); | 
|  | } | 
|  |  | 
|  | static struct i2c_driver tda1997x_i2c_driver = { | 
|  | .driver = { | 
|  | .name = "tda1997x", | 
|  | .of_match_table = of_match_ptr(tda1997x_of_id), | 
|  | }, | 
|  | .probe = tda1997x_probe, | 
|  | .remove = tda1997x_remove, | 
|  | .id_table = tda1997x_i2c_id, | 
|  | }; | 
|  |  | 
|  | module_i2c_driver(tda1997x_i2c_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); | 
|  | MODULE_DESCRIPTION("TDA1997X HDMI Receiver driver"); | 
|  | MODULE_LICENSE("GPL v2"); |