summaryrefslogtreecommitdiffstats
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.c156
1 files changed, 95 insertions, 61 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 1d675b58fd7..fbfdd2fc2a3 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -38,6 +38,7 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dv-timings.h>
#include <media/adv7604.h>
static int debug;
@@ -76,6 +77,7 @@ struct adv7604_state {
struct delayed_work delayed_work_enable_hotplug;
bool connector_hdmi;
bool restart_stdi_once;
+ u32 prev_input_status;
/* i2c clients */
struct i2c_client *i2c_avlink;
@@ -260,22 +262,22 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
static inline unsigned hblanking(const struct v4l2_bt_timings *t)
{
- return t->hfrontporch + t->hsync + t->hbackporch;
+ return V4L2_DV_BT_BLANKING_WIDTH(t);
}
static inline unsigned htotal(const struct v4l2_bt_timings *t)
{
- return t->width + t->hfrontporch + t->hsync + t->hbackporch;
+ return V4L2_DV_BT_FRAME_WIDTH(t);
}
static inline unsigned vblanking(const struct v4l2_bt_timings *t)
{
- return t->vfrontporch + t->vsync + t->vbackporch;
+ return V4L2_DV_BT_BLANKING_HEIGHT(t);
}
static inline unsigned vtotal(const struct v4l2_bt_timings *t)
{
- return t->height + t->vfrontporch + t->vsync + t->vbackporch;
+ return V4L2_DV_BT_FRAME_HEIGHT(t);
}
/* ----------------------------------------------------------------------- */
@@ -761,7 +763,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
int i;
for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
- if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings,
+ if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
DIGITAL_INPUT ? 250000 : 1000000))
continue;
io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */
@@ -990,6 +992,11 @@ static inline bool no_lock_tmds(struct v4l2_subdev *sd)
return (io_read(sd, 0x6a) & 0xe0) != 0xe0;
}
+static inline bool is_hdmi(struct v4l2_subdev *sd)
+{
+ return hdmi_read(sd, 0x05) & 0x80;
+}
+
static inline bool no_lock_sspd(struct v4l2_subdev *sd)
{
/* TODO channel 2 */
@@ -1044,38 +1051,6 @@ static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)
/* ----------------------------------------------------------------------- */
-static void adv7604_print_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings, const char *txt, bool detailed)
-{
- struct v4l2_bt_timings *bt = &timings->bt;
- u32 htot, vtot;
-
- if (timings->type != V4L2_DV_BT_656_1120)
- return;
-
- htot = htotal(bt);
- vtot = vtotal(bt);
-
- v4l2_info(sd, "%s %dx%d%s%d (%dx%d)",
- txt, bt->width, bt->height, bt->interlaced ? "i" : "p",
- (htot * vtot) > 0 ? ((u32)bt->pixelclock /
- (htot * vtot)) : 0,
- htot, vtot);
-
- if (detailed) {
- v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n",
- bt->hfrontporch,
- (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-",
- bt->hsync, bt->hbackporch);
- v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n",
- bt->vfrontporch,
- (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
- bt->vsync, bt->vbackporch);
- v4l2_info(sd, " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n",
- bt->pixelclock, bt->flags, bt->standards);
- }
-}
-
struct stdi_readback {
u16 bl, lcf, lcvs;
u8 hs_pol, vs_pol;
@@ -1187,7 +1162,7 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1200;
- cap->bt.min_pixelclock = 27000000;
+ cap->bt.min_pixelclock = 25000000;
if (DIGITAL_INPUT)
cap->bt.max_pixelclock = 225000000;
else
@@ -1208,7 +1183,7 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
int i;
for (i = 0; adv7604_timings[i].bt.width; i++) {
- if (v4l_match_dv_timings(timings, &adv7604_timings[i],
+ if (v4l2_match_dv_timings(timings, &adv7604_timings[i],
DIGITAL_INPUT ? 250000 : 1000000)) {
*timings = adv7604_timings[i];
break;
@@ -1242,12 +1217,21 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
if (DIGITAL_INPUT) {
+ uint32_t freq;
+
timings->type = V4L2_DV_BT_656_1120;
bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
- bt->pixelclock = (hdmi_read(sd, 0x06) * 1000000) +
+ freq = (hdmi_read(sd, 0x06) * 1000000) +
((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
+ if (is_hdmi(sd)) {
+ /* adjust for deep color mode */
+ unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8;
+
+ freq = freq * 8 / bits_per_channel;
+ }
+ bt->pixelclock = freq;
bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
hdmi_read(sd, 0x21);
bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +
@@ -1329,8 +1313,8 @@ found:
}
if (debug > 1)
- adv7604_print_timings(sd, timings,
- "adv7604_query_dv_timings:", true);
+ v4l2_print_dv_timings(sd->name, "adv7604_query_dv_timings: ",
+ timings, true);
return 0;
}
@@ -1372,8 +1356,8 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
if (debug > 1)
- adv7604_print_timings(sd, timings,
- "adv7604_s_dv_timings:", true);
+ v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ",
+ timings, true);
return 0;
}
@@ -1534,6 +1518,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
{
struct adv7604_state *state = to_state(sd);
u8 fmt_change, fmt_change_digital, tx_5v;
+ u32 input_status;
/* format change */
fmt_change = io_read(sd, 0x43) & 0x98;
@@ -1544,9 +1529,18 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
io_write(sd, 0x6c, fmt_change_digital);
if (fmt_change || fmt_change_digital) {
v4l2_dbg(1, debug, sd,
- "%s: ADV7604_FMT_CHANGE, fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
+ "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
__func__, fmt_change, fmt_change_digital);
- v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
+
+ adv7604_g_input_status(sd, &input_status);
+ if (input_status != state->prev_input_status) {
+ v4l2_dbg(1, debug, sd,
+ "%s: input_status = 0x%x, prev_input_status = 0x%x\n",
+ __func__, input_status, state->prev_input_status);
+ state->prev_input_status = input_status;
+ v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
+ }
+
if (handled)
*handled = true;
}
@@ -1625,7 +1619,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
u8 avi_len;
u8 avi_ver;
- if (!(hdmi_read(sd, 0x05) & 0x80)) {
+ if (!is_hdmi(sd)) {
v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n");
return;
}
@@ -1686,6 +1680,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
"RGB limited range (16-235)",
"RGB full range (0-255)",
};
+ char *deep_color_mode_txt[4] = {
+ "8-bits per channel",
+ "10-bits per channel",
+ "12-bits per channel",
+ "16-bits per channel (not supported)"
+ };
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
@@ -1723,8 +1723,13 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
if (adv7604_query_dv_timings(sd, &timings))
v4l2_info(sd, "No video detected\n");
else
- adv7604_print_timings(sd, &timings, "Detected format:", true);
- adv7604_print_timings(sd, &state->timings, "Configured format:", true);
+ v4l2_print_dv_timings(sd->name, "Detected format: ",
+ &timings, true);
+ v4l2_print_dv_timings(sd->name, "Configured format: ",
+ &state->timings, true);
+
+ if (no_signal(sd))
+ return 0;
v4l2_info(sd, "-----Color space-----\n");
v4l2_info(sd, "RGB quantization range ctrl: %s\n",
@@ -1735,15 +1740,40 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
(reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
(reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ?
- "enabled" : "disabled");
+ "enabled" : "disabled");
v4l2_info(sd, "Color space conversion: %s\n",
csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]);
- /* Digital video */
- if (DIGITAL_INPUT) {
- v4l2_info(sd, "-----HDMI status-----\n");
- v4l2_info(sd, "HDCP encrypted content: %s\n",
- hdmi_read(sd, 0x05) & 0x40 ? "true" : "false");
+ if (!DIGITAL_INPUT)
+ return 0;
+
+ v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D");
+ v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");
+ v4l2_info(sd, "HDCP keys read: %s%s\n",
+ (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no",
+ (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : "");
+ if (!is_hdmi(sd)) {
+ bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01;
+ bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01;
+ bool audio_mute = io_read(sd, 0x65) & 0x40;
+
+ v4l2_info(sd, "Audio: pll %s, samples %s, %s\n",
+ audio_pll_locked ? "locked" : "not locked",
+ audio_sample_packet_detect ? "detected" : "not detected",
+ audio_mute ? "muted" : "enabled");
+ if (audio_pll_locked && audio_sample_packet_detect) {
+ v4l2_info(sd, "Audio format: %s\n",
+ (hdmi_read(sd, 0x07) & 0x20) ? "multi-channel" : "stereo");
+ }
+ v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) +
+ (hdmi_read(sd, 0x5c) << 8) +
+ (hdmi_read(sd, 0x5d) & 0xf0));
+ v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) +
+ (hdmi_read(sd, 0x5e) << 8) +
+ hdmi_read(sd, 0x5f));
+ v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off");
+
+ v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]);
print_avi_infoframe(sd);
}
@@ -1952,6 +1982,10 @@ static int adv7604_probe(struct i2c_client *client,
return -ENOMEM;
}
+ /* initialize variables */
+ state->restart_stdi_once = true;
+ state->prev_input_status = ~0;
+
/* platform data */
if (!pdata) {
v4l_err(client, "No platform data!\n");
@@ -1987,29 +2021,30 @@ static int adv7604_probe(struct i2c_client *client,
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
- state->detect_tx_5v_ctrl->is_private = true;
state->rgb_quantization_range_ctrl =
v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
0, V4L2_DV_RGB_RANGE_AUTO);
- state->rgb_quantization_range_ctrl->is_private = true;
/* custom controls */
state->analog_sampling_phase_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
- state->analog_sampling_phase_ctrl->is_private = true;
state->free_run_color_manual_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);
- state->free_run_color_manual_ctrl->is_private = true;
state->free_run_color_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL);
- state->free_run_color_ctrl->is_private = true;
sd->ctrl_handler = hdl;
if (hdl->error) {
err = hdl->error;
goto err_hdl;
}
+ state->detect_tx_5v_ctrl->is_private = true;
+ state->rgb_quantization_range_ctrl->is_private = true;
+ state->analog_sampling_phase_ctrl->is_private = true;
+ state->free_run_color_manual_ctrl->is_private = true;
+ state->free_run_color_ctrl->is_private = true;
+
if (adv7604_s_detect_tx_5v_ctrl(sd)) {
err = -ENODEV;
goto err_hdl;
@@ -2035,7 +2070,6 @@ static int adv7604_probe(struct i2c_client *client,
v4l2_err(sd, "failed to create all i2c clients\n");
goto err_i2c;
}
- state->restart_stdi_once = true;
/* work queues */
state->work_queues = create_singlethread_workqueue(client->name);