summaryrefslogtreecommitdiff
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.c143
1 files changed, 101 insertions, 42 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 319db3e847c4..e271782b7b70 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -42,7 +42,7 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-2)");
MODULE_DESCRIPTION("Analog Devices ADV7604/10/11/12 video decoder driver");
-MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>");
MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>");
MODULE_LICENSE("GPL");
@@ -193,6 +193,9 @@ struct adv76xx_state {
struct delayed_work delayed_work_enable_hotplug;
bool restart_stdi_once;
+ struct dentry *debugfs_dir;
+ struct v4l2_debugfs_if *infoframes;
+
/* CEC */
struct cec_adapter *cec_adap;
u8 cec_addr[ADV76XX_MAX_ADDRS];
@@ -1405,12 +1408,13 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0,
(stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
(stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
- false, timings))
+ false, adv76xx_get_dv_timings_cap(sd, -1), timings))
return 0;
if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs,
(stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
(stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
- false, state->aspect_ratio, timings))
+ false, state->aspect_ratio,
+ adv76xx_get_dv_timings_cap(sd, -1), timings))
return 0;
v4l2_dbg(2, debug, sd,
@@ -1557,8 +1561,8 @@ static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd)
return freq;
}
-static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
const struct adv76xx_chip_info *info = state->info;
@@ -1687,8 +1691,8 @@ found:
return 0;
}
-static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
struct v4l2_bt_timings *bt;
@@ -1730,8 +1734,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
@@ -2458,10 +2462,9 @@ static const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = {
{ "Vendor", 0x10, 0xec, 0x54 }
};
-static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index,
- union hdmi_infoframe *frame)
+static int adv76xx_read_infoframe_buf(struct v4l2_subdev *sd, int index,
+ u8 buf[V4L2_DEBUGFS_IF_MAX_LEN])
{
- uint8_t buffer[32];
u8 len;
int i;
@@ -2472,27 +2475,20 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index,
}
for (i = 0; i < 3; i++)
- buffer[i] = infoframe_read(sd,
- adv76xx_cri[index].head_addr + i);
+ buf[i] = infoframe_read(sd, adv76xx_cri[index].head_addr + i);
- len = buffer[2] + 1;
+ len = buf[2] + 1;
- if (len + 3 > sizeof(buffer)) {
+ if (len + 3 > V4L2_DEBUGFS_IF_MAX_LEN) {
v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__,
adv76xx_cri[index].desc, len);
return -ENOENT;
}
for (i = 0; i < len; i++)
- buffer[i + 3] = infoframe_read(sd,
- adv76xx_cri[index].payload_addr + i);
-
- if (hdmi_infoframe_unpack(frame, buffer, len + 3) < 0) {
- v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__,
- adv76xx_cri[index].desc);
- return -ENOENT;
- }
- return 0;
+ buf[i + 3] = infoframe_read(sd,
+ adv76xx_cri[index].payload_addr + i);
+ return len + 3;
}
static void adv76xx_log_infoframes(struct v4l2_subdev *sd)
@@ -2505,10 +2501,19 @@ static void adv76xx_log_infoframes(struct v4l2_subdev *sd)
}
for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) {
- union hdmi_infoframe frame;
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {};
+ union hdmi_infoframe frame;
+ int len;
+
+ len = adv76xx_read_infoframe_buf(sd, i, buffer);
+ if (len < 0)
+ continue;
- if (!adv76xx_read_infoframe(sd, i, &frame))
+ if (hdmi_infoframe_unpack(&frame, buffer, len) < 0)
+ v4l2_err(sd, "%s: unpack of %s infoframe failed\n",
+ __func__, adv76xx_cri[i].desc);
+ else
hdmi_infoframe_log(KERN_INFO, &client->dev, &frame);
}
}
@@ -2519,10 +2524,10 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
const struct adv76xx_chip_info *info = state->info;
struct v4l2_dv_timings timings;
struct stdi_readback stdi;
- u8 reg_io_0x02 = io_read(sd, 0x02);
+ int ret;
+ u8 reg_io_0x02;
u8 edid_enabled;
u8 cable_det;
-
static const char * const csc_coeff_sel_rb[16] = {
"bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB",
"reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709",
@@ -2607,7 +2612,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
stdi.lcf, stdi.bl, stdi.lcvs,
stdi.interlaced ? "interlaced" : "progressive",
stdi.hs_pol, stdi.vs_pol);
- if (adv76xx_query_dv_timings(sd, &timings))
+ if (adv76xx_query_dv_timings(sd, 0, &timings))
v4l2_info(sd, "No video detected\n");
else
v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -2621,13 +2626,21 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "-----Color space-----\n");
v4l2_info(sd, "RGB quantization range ctrl: %s\n",
rgb_quantization_range_txt[state->rgb_quantization_range]);
- v4l2_info(sd, "Input color space: %s\n",
- input_color_space_txt[reg_io_0x02 >> 4]);
- v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
- (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
- (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
- "(16-235)" : "(0-255)",
- (reg_io_0x02 & 0x08) ? "enabled" : "disabled");
+
+ ret = io_read(sd, 0x02);
+ if (ret < 0) {
+ v4l2_info(sd, "Can't read Input/Output color space\n");
+ } else {
+ reg_io_0x02 = ret;
+
+ v4l2_info(sd, "Input color space: %s\n",
+ input_color_space_txt[reg_io_0x02 >> 4]);
+ v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
+ (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
+ (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
+ "(16-235)" : "(0-255)",
+ (reg_io_0x02 & 0x08) ? "enabled" : "disabled");
+ }
v4l2_info(sd, "Color space conversion: %s\n",
csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
@@ -2686,6 +2699,41 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
}
}
+static ssize_t
+adv76xx_debugfs_if_read(u32 type, void *priv, struct file *filp,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {};
+ struct v4l2_subdev *sd = priv;
+ int index;
+ int len;
+
+ if (!is_hdmi(sd))
+ return 0;
+
+ switch (type) {
+ case V4L2_DEBUGFS_IF_AVI:
+ index = 0;
+ break;
+ case V4L2_DEBUGFS_IF_AUDIO:
+ index = 1;
+ break;
+ case V4L2_DEBUGFS_IF_SPD:
+ index = 2;
+ break;
+ case V4L2_DEBUGFS_IF_HDMI:
+ index = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ len = adv76xx_read_infoframe_buf(sd, index, buf);
+ if (len > 0)
+ len = simple_read_from_buffer(ubuf, count, ppos, buf, len);
+ return len < 0 ? 0 : len;
+}
+
static int adv76xx_registered(struct v4l2_subdev *sd)
{
struct adv76xx_state *state = to_state(sd);
@@ -2693,9 +2741,16 @@ static int adv76xx_registered(struct v4l2_subdev *sd)
int err;
err = cec_register_adapter(state->cec_adap, &client->dev);
- if (err)
+ if (err) {
cec_delete_adapter(state->cec_adap);
- return err;
+ return err;
+ }
+ state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root());
+ state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir,
+ V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO |
+ V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI, sd,
+ adv76xx_debugfs_if_read);
+ return 0;
}
static void adv76xx_unregistered(struct v4l2_subdev *sd)
@@ -2703,6 +2758,10 @@ static void adv76xx_unregistered(struct v4l2_subdev *sd)
struct adv76xx_state *state = to_state(sd);
cec_unregister_adapter(state->cec_adap);
+ v4l2_debugfs_if_free(state->infoframes);
+ state->infoframes = NULL;
+ debugfs_remove_recursive(state->debugfs_dir);
+ state->debugfs_dir = NULL;
}
/* ----------------------------------------------------------------------- */
@@ -2726,9 +2785,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
.s_routing = adv76xx_s_routing,
.g_input_status = adv76xx_g_input_status,
- .s_dv_timings = adv76xx_s_dv_timings,
- .g_dv_timings = adv76xx_g_dv_timings,
- .query_dv_timings = adv76xx_query_dv_timings,
};
static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
@@ -2738,6 +2794,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
.set_fmt = adv76xx_set_format,
.get_edid = adv76xx_get_edid,
.set_edid = adv76xx_set_edid,
+ .s_dv_timings = adv76xx_s_dv_timings,
+ .g_dv_timings = adv76xx_g_dv_timings,
+ .query_dv_timings = adv76xx_query_dv_timings,
.dv_timings_cap = adv76xx_dv_timings_cap,
.enum_dv_timings = adv76xx_enum_dv_timings,
};