aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/adv7604.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/adv7604.c')
-rw-r--r--drivers/media/i2c/adv7604.c486
1 files changed, 381 insertions, 105 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 808360fd6539..5631ec004eed 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -37,10 +37,12 @@
#include <linux/v4l2-dv-timings.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
+#include <linux/regmap.h>
#include <media/adv7604.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-of.h>
@@ -81,6 +83,7 @@ MODULE_LICENSE("GPL");
enum adv76xx_type {
ADV7604,
ADV7611,
+ ADV7612,
};
struct adv76xx_reg_seq {
@@ -188,6 +191,9 @@ struct adv76xx_state {
/* i2c clients */
struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
+ /* Regmaps */
+ struct regmap *regmap[ADV76XX_PAGE_MAX];
+
/* controls */
struct v4l2_ctrl *detect_tx_5v_ctrl;
struct v4l2_ctrl *analog_sampling_phase_ctrl;
@@ -373,66 +379,39 @@ static inline unsigned vtotal(const struct v4l2_bt_timings *t)
/* ----------------------------------------------------------------------- */
-static s32 adv_smbus_read_byte_data_check(struct i2c_client *client,
- u8 command, bool check)
-{
- union i2c_smbus_data data;
-
- if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags,
- I2C_SMBUS_READ, command,
- I2C_SMBUS_BYTE_DATA, &data))
- return data.byte;
- if (check)
- v4l_err(client, "error reading %02x, %02x\n",
- client->addr, command);
- return -EIO;
-}
-
-static s32 adv_smbus_read_byte_data(struct adv76xx_state *state,
- enum adv76xx_page page, u8 command)
+static int adv76xx_read_check(struct adv76xx_state *state,
+ int client_page, u8 reg)
{
- return adv_smbus_read_byte_data_check(state->i2c_clients[page],
- command, true);
-}
-
-static s32 adv_smbus_write_byte_data(struct adv76xx_state *state,
- enum adv76xx_page page, u8 command,
- u8 value)
-{
- struct i2c_client *client = state->i2c_clients[page];
- union i2c_smbus_data data;
+ struct i2c_client *client = state->i2c_clients[client_page];
int err;
- int i;
+ unsigned int val;
- data.byte = value;
- for (i = 0; i < 3; i++) {
- err = i2c_smbus_xfer(client->adapter, client->addr,
- client->flags,
- I2C_SMBUS_WRITE, command,
- I2C_SMBUS_BYTE_DATA, &data);
- if (!err)
- break;
+ err = regmap_read(state->regmap[client_page], reg, &val);
+
+ if (err) {
+ v4l_err(client, "error reading %02x, %02x\n",
+ client->addr, reg);
+ return err;
}
- if (err < 0)
- v4l_err(client, "error writing %02x, %02x, %02x\n",
- client->addr, command, value);
- return err;
+ return val;
}
-static s32 adv_smbus_write_i2c_block_data(struct adv76xx_state *state,
- enum adv76xx_page page, u8 command,
- unsigned length, const u8 *values)
+/* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
+ * size to one or more registers.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+static int adv76xx_write_block(struct adv76xx_state *state, int client_page,
+ unsigned int init_reg, const void *val,
+ size_t val_len)
{
- struct i2c_client *client = state->i2c_clients[page];
- union i2c_smbus_data data;
+ struct regmap *regmap = state->regmap[client_page];
+
+ if (val_len > I2C_SMBUS_BLOCK_MAX)
+ val_len = I2C_SMBUS_BLOCK_MAX;
- if (length > I2C_SMBUS_BLOCK_MAX)
- length = I2C_SMBUS_BLOCK_MAX;
- data.block[0] = length;
- memcpy(data.block + 1, values, length);
- return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
- I2C_SMBUS_WRITE, command,
- I2C_SMBUS_I2C_BLOCK_DATA, &data);
+ return regmap_raw_write(regmap, init_reg, val, val_len);
}
/* ----------------------------------------------------------------------- */
@@ -441,14 +420,14 @@ static inline int io_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_IO, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_IO, reg);
}
static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val);
}
static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -460,71 +439,70 @@ static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg);
+ return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg);
}
static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val);
+ return regmap_write(state->regmap[ADV7604_PAGE_AVLINK], reg, val);
}
static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CEC, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_CEC, reg);
}
static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
}
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg);
}
static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_INFOFRAME,
- reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val);
}
static inline int afe_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_AFE, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_AFE, reg);
}
static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_AFE, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_AFE], reg, val);
}
static inline int rep_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_REP, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_REP, reg);
}
static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_REP, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_REP], reg, val);
}
static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -536,28 +514,37 @@ static inline int edid_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_EDID, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg);
}
static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_EDID, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_EDID], reg, val);
}
static inline int edid_write_block(struct v4l2_subdev *sd,
- unsigned len, const u8 *val)
+ unsigned int total_len, const u8 *val)
{
struct adv76xx_state *state = to_state(sd);
int err = 0;
- int i;
+ int i = 0;
+ int len = 0;
+
+ v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n",
+ __func__, total_len);
- v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len);
+ while (!err && i < total_len) {
+ len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ?
+ I2C_SMBUS_BLOCK_MAX :
+ (total_len - i);
+
+ err = adv76xx_write_block(state, ADV76XX_PAGE_EDID,
+ i, val + i, len);
+ i += len;
+ }
- for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX)
- err = adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_EDID,
- i, I2C_SMBUS_BLOCK_MAX, val + i);
return err;
}
@@ -587,7 +574,7 @@ static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_HDMI, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_HDMI, reg);
}
static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
@@ -599,7 +586,7 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_HDMI, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_HDMI], reg, val);
}
static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -611,14 +598,14 @@ static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_TEST, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_TEST], reg, val);
}
static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CP, reg);
+ return adv76xx_read_check(state, ADV76XX_PAGE_CP, reg);
}
static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
@@ -630,7 +617,7 @@ static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CP, reg, val);
+ return regmap_write(state->regmap[ADV76XX_PAGE_CP], reg, val);
}
static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -642,14 +629,14 @@ static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg);
+ return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg);
}
static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv76xx_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val);
+ return regmap_write(state->regmap[ADV7604_PAGE_VDP], reg, val);
}
#define ADV76XX_REG(page, offset) (((page) << 8) | (offset))
@@ -660,13 +647,16 @@ static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg)
{
struct adv76xx_state *state = to_state(sd);
unsigned int page = reg >> 8;
+ unsigned int val;
+ int err;
if (!(BIT(page) & state->info->page_mask))
return -EINVAL;
reg &= 0xff;
+ err = regmap_read(state->regmap[page], reg, &val);
- return adv_smbus_read_byte_data(state, page, reg);
+ return err ? err : val;
}
#endif
@@ -680,7 +670,7 @@ static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
reg &= 0xff;
- return adv_smbus_write_byte_data(state, page, reg, val);
+ return regmap_write(state->regmap[page], reg, val);
}
static void adv76xx_write_reg_seq(struct v4l2_subdev *sd,
@@ -766,6 +756,23 @@ static const struct adv76xx_format_info adv7611_formats[] = {
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
};
+static const struct adv76xx_format_info adv7612_formats[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
+ ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
+ ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
+ ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
+ ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
+ ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
+ ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
+ ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+};
+
static const struct adv76xx_format_info *
adv76xx_format_info(struct adv76xx_state *state, u32 code)
{
@@ -870,6 +877,16 @@ static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd)
return value & 1;
}
+static unsigned int adv7612_read_cable_det(struct v4l2_subdev *sd)
+{
+ /* Reads CABLE_DET_A_RAW. For input B support, need to
+ * account for bit 7 [MSB] of 0x6a (ie. CABLE_DET_B_RAW)
+ */
+ u8 value = io_read(sd, 0x6f);
+
+ return value & 1;
+}
+
static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
{
struct adv76xx_state *state = to_state(sd);
@@ -976,8 +993,8 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
/* IO-map reg. 0x16 and 0x17 should be written in sequence */
- if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_IO,
- 0x16, 2, pll))
+ if (regmap_raw_write(state->regmap[ADV76XX_PAGE_IO],
+ 0x16, pll, 2))
v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
/* active video - horizontal timing */
@@ -1028,8 +1045,8 @@ static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off
offset_buf[3] = offset_c & 0x0ff;
/* Registers must be written in this order with no i2c access in between */
- if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP,
- 0x77, 4, offset_buf))
+ if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP],
+ 0x77, offset_buf, 4))
v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
}
@@ -1058,8 +1075,8 @@ static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a,
gain_buf[3] = ((gain_c & 0x0ff));
/* Registers must be written in this order with no i2c access in between */
- if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP,
- 0x73, 4, gain_buf))
+ if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP],
+ 0x73, gain_buf, 4))
v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
}
@@ -1328,7 +1345,7 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
}
}
- if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs,
+ if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0,
(stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
(stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
false, timings))
@@ -1760,8 +1777,8 @@ static int adv76xx_s_routing(struct v4l2_subdev *sd,
select_input(sd);
enable_input(sd);
- v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT,
- (void *)&adv76xx_ev_fmt);
+ v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt);
+
return 0;
}
@@ -1928,8 +1945,7 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
__func__, fmt_change, fmt_change_digital);
- v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT,
- (void *)&adv76xx_ev_fmt);
+ v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt);
if (handled)
*handled = true;
@@ -2347,6 +2363,20 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
return 0;
}
+static int adv76xx_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_ctrl_ops adv76xx_ctrl_ops = {
@@ -2356,6 +2386,8 @@ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
.log_status = adv76xx_log_status,
.interrupt_service_routine = adv76xx_isr,
+ .subscribe_event = adv76xx_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv76xx_g_register,
.s_register = adv76xx_s_register,
@@ -2510,6 +2542,11 @@ static void adv7611_setup_irqs(struct v4l2_subdev *sd)
io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */
}
+static void adv7612_setup_irqs(struct v4l2_subdev *sd)
+{
+ io_write(sd, 0x41, 0xd0); /* disable INT2 */
+}
+
static void adv76xx_unregister_clients(struct adv76xx_state *state)
{
unsigned int i;
@@ -2597,6 +2634,19 @@ static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = {
{ ADV76XX_REG_SEQ_TERM, 0 },
};
+static const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi[] = {
+ { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 },
+ { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 },
+ { ADV76XX_REG_SEQ_TERM, 0 },
+};
+
static const struct adv76xx_chip_info adv76xx_chip_info[] = {
[ADV7604] = {
.type = ADV7604,
@@ -2685,17 +2735,60 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = {
.field1_vsync_mask = 0x3fff,
.field1_vbackporch_mask = 0x3fff,
},
+ [ADV7612] = {
+ .type = ADV7612,
+ .has_afe = false,
+ .max_port = ADV76XX_PAD_HDMI_PORT_A, /* B not supported */
+ .num_dv_ports = 1, /* normally 2 */
+ .edid_enable_reg = 0x74,
+ .edid_status_reg = 0x76,
+ .lcf_reg = 0xa3,
+ .tdms_lock_mask = 0x43,
+ .cable_det_mask = 0x01,
+ .fmt_change_digital_mask = 0x03,
+ .cp_csc = 0xf4,
+ .formats = adv7612_formats,
+ .nformats = ARRAY_SIZE(adv7612_formats),
+ .set_termination = adv7611_set_termination,
+ .setup_irqs = adv7612_setup_irqs,
+ .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock,
+ .read_cable_det = adv7612_read_cable_det,
+ .recommended_settings = {
+ [1] = adv7612_recommended_settings_hdmi,
+ },
+ .num_recommended_settings = {
+ [1] = ARRAY_SIZE(adv7612_recommended_settings_hdmi),
+ },
+ .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) |
+ BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) |
+ BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) |
+ BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP),
+ .linewidth_mask = 0x1fff,
+ .field0_height_mask = 0x1fff,
+ .field1_height_mask = 0x1fff,
+ .hfrontporch_mask = 0x1fff,
+ .hsync_mask = 0x1fff,
+ .hbackporch_mask = 0x1fff,
+ .field0_vfrontporch_mask = 0x3fff,
+ .field0_vsync_mask = 0x3fff,
+ .field0_vbackporch_mask = 0x3fff,
+ .field1_vfrontporch_mask = 0x3fff,
+ .field1_vsync_mask = 0x3fff,
+ .field1_vbackporch_mask = 0x3fff,
+ },
};
static const struct i2c_device_id adv76xx_i2c_id[] = {
{ "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] },
{ "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
+ { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id);
static const struct of_device_id adv76xx_of_id[] __maybe_unused = {
{ .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] },
+ { .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] },
{ }
};
MODULE_DEVICE_TABLE(of, adv76xx_of_id);
@@ -2706,6 +2799,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
struct device_node *endpoint;
struct device_node *np;
unsigned int flags;
+ u32 v;
np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node;
@@ -2715,6 +2809,12 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
return -EINVAL;
v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+
+ if (!of_property_read_u32(endpoint, "default-input", &v))
+ state->pdata.default_input = v;
+ else
+ state->pdata.default_input = -1;
+
of_node_put(endpoint);
flags = bus_cfg.bus.parallel.flags;
@@ -2753,7 +2853,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
/* Hardcode the remaining platform data fields. */
state->pdata.disable_pwrdnb = 0;
state->pdata.disable_cable_det_rst = 0;
- state->pdata.default_input = -1;
state->pdata.blank_data = 1;
state->pdata.alt_data_sat = 1;
state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
@@ -2762,6 +2861,148 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
return 0;
}
+static const struct regmap_config adv76xx_regmap_cnf[] = {
+ {
+ .name = "io",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "avlink",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "cec",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "infoframe",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "esdp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "epp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "afe",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "rep",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "edid",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+
+ {
+ .name = "hdmi",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "test",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "cp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "vdp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+};
+
+static int configure_regmap(struct adv76xx_state *state, int region)
+{
+ int err;
+
+ if (!state->i2c_clients[region])
+ return -ENODEV;
+
+ state->regmap[region] =
+ devm_regmap_init_i2c(state->i2c_clients[region],
+ &adv76xx_regmap_cnf[region]);
+
+ if (IS_ERR(state->regmap[region])) {
+ err = PTR_ERR(state->regmap[region]);
+ v4l_err(state->i2c_clients[region],
+ "Error initializing regmap %d with error %d\n",
+ region, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int configure_regmaps(struct adv76xx_state *state)
+{
+ int i, err;
+
+ for (i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX; i++) {
+ err = configure_regmap(state, i);
+ if (err && (err != -ENODEV))
+ return err;
+ }
+ return 0;
+}
+
static int adv76xx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -2771,7 +3012,7 @@ static int adv76xx_probe(struct i2c_client *client,
struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
unsigned int i;
- u16 val;
+ unsigned int val, val2;
int err;
/* Check if the adapter supports the needed features */
@@ -2833,28 +3074,59 @@ static int adv76xx_probe(struct i2c_client *client,
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;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+
+ /* Configure IO Regmap region */
+ err = configure_regmap(state, ADV76XX_PAGE_IO);
+
+ if (err) {
+ v4l2_err(sd, "Error configuring IO regmap region\n");
+ return -ENODEV;
+ }
/*
* Verify that the chip is present. On ADV7604 the RD_INFO register only
* identifies the revision, while on ADV7611 it identifies the model as
* well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611.
*/
- if (state->info->type == ADV7604) {
- val = adv_smbus_read_byte_data_check(client, 0xfb, false);
+ switch (state->info->type) {
+ case ADV7604:
+ err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val);
+ if (err) {
+ v4l2_err(sd, "Error %d reading IO Regmap\n", err);
+ return -ENODEV;
+ }
if (val != 0x68) {
- v4l2_info(sd, "not an adv7604 on address 0x%x\n",
+ v4l2_err(sd, "not an adv7604 on address 0x%x\n",
client->addr << 1);
return -ENODEV;
}
- } else {
- val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8)
- | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0);
- if (val != 0x2051) {
- v4l2_info(sd, "not an adv7611 on address 0x%x\n",
+ break;
+ case ADV7611:
+ case ADV7612:
+ err = regmap_read(state->regmap[ADV76XX_PAGE_IO],
+ 0xea,
+ &val);
+ if (err) {
+ v4l2_err(sd, "Error %d reading IO Regmap\n", err);
+ return -ENODEV;
+ }
+ val2 = val << 8;
+ err = regmap_read(state->regmap[ADV76XX_PAGE_IO],
+ 0xeb,
+ &val);
+ if (err) {
+ v4l2_err(sd, "Error %d reading IO Regmap\n", err);
+ return -ENODEV;
+ }
+ val |= val2;
+ if ((state->info->type == ADV7611 && val != 0x2051) ||
+ (state->info->type == ADV7612 && val != 0x2041)) {
+ v4l2_err(sd, "not an adv761x on address 0x%x\n",
client->addr << 1);
return -ENODEV;
}
+ break;
}
/* control handlers */
@@ -2941,6 +3213,11 @@ static int adv76xx_probe(struct i2c_client *client,
if (err)
goto err_work_queues;
+ /* Configure regmaps */
+ err = configure_regmaps(state);
+ if (err)
+ goto err_entity;
+
err = adv76xx_core_init(sd);
if (err)
goto err_entity;
@@ -2985,7 +3262,6 @@ static int adv76xx_remove(struct i2c_client *client)
static struct i2c_driver adv76xx_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "adv7604",
.of_match_table = of_match_ptr(adv76xx_of_id),
},