13e89586aSKieran Bingham /*
23e89586aSKieran Bingham  * Driver for Analog Devices ADV748X 8 channel analog front end (AFE) receiver
33e89586aSKieran Bingham  * with standard definition processor (SDP)
43e89586aSKieran Bingham  *
53e89586aSKieran Bingham  * Copyright (C) 2017 Renesas Electronics Corp.
63e89586aSKieran Bingham  *
73e89586aSKieran Bingham  * This program is free software; you can redistribute  it and/or modify it
83e89586aSKieran Bingham  * under  the terms of  the GNU General  Public License as published by the
93e89586aSKieran Bingham  * Free Software Foundation;  either version 2 of the  License, or (at your
103e89586aSKieran Bingham  * option) any later version.
113e89586aSKieran Bingham  */
123e89586aSKieran Bingham 
133e89586aSKieran Bingham #include <linux/delay.h>
143e89586aSKieran Bingham #include <linux/module.h>
153e89586aSKieran Bingham #include <linux/mutex.h>
163e89586aSKieran Bingham #include <linux/v4l2-dv-timings.h>
173e89586aSKieran Bingham 
183e89586aSKieran Bingham #include <media/v4l2-ctrls.h>
193e89586aSKieran Bingham #include <media/v4l2-device.h>
203e89586aSKieran Bingham #include <media/v4l2-dv-timings.h>
213e89586aSKieran Bingham #include <media/v4l2-ioctl.h>
223e89586aSKieran Bingham 
233e89586aSKieran Bingham #include "adv748x.h"
243e89586aSKieran Bingham 
253e89586aSKieran Bingham /* -----------------------------------------------------------------------------
263e89586aSKieran Bingham  * SDP
273e89586aSKieran Bingham  */
283e89586aSKieran Bingham 
293e89586aSKieran Bingham #define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
303e89586aSKieran Bingham #define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM_PED	0x1
313e89586aSKieran Bingham #define ADV748X_AFE_STD_AD_PAL_N_NTSC_J_SECAM		0x2
323e89586aSKieran Bingham #define ADV748X_AFE_STD_AD_PAL_N_NTSC_M_SECAM		0x3
333e89586aSKieran Bingham #define ADV748X_AFE_STD_NTSC_J				0x4
343e89586aSKieran Bingham #define ADV748X_AFE_STD_NTSC_M				0x5
353e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL60				0x6
363e89586aSKieran Bingham #define ADV748X_AFE_STD_NTSC_443			0x7
373e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_BG				0x8
383e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_N				0x9
393e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_M				0xa
403e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_M_PED			0xb
413e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_COMB_N			0xc
423e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_COMB_N_PED			0xd
433e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_SECAM			0xe
443e89586aSKieran Bingham #define ADV748X_AFE_STD_PAL_SECAM_PED			0xf
453e89586aSKieran Bingham 
463e89586aSKieran Bingham static int adv748x_afe_read_ro_map(struct adv748x_state *state, u8 reg)
473e89586aSKieran Bingham {
483e89586aSKieran Bingham 	int ret;
493e89586aSKieran Bingham 
503e89586aSKieran Bingham 	/* Select SDP Read-Only Main Map */
513e89586aSKieran Bingham 	ret = sdp_write(state, ADV748X_SDP_MAP_SEL,
523e89586aSKieran Bingham 			ADV748X_SDP_MAP_SEL_RO_MAIN);
533e89586aSKieran Bingham 	if (ret < 0)
543e89586aSKieran Bingham 		return ret;
553e89586aSKieran Bingham 
563e89586aSKieran Bingham 	return sdp_read(state, reg);
573e89586aSKieran Bingham }
583e89586aSKieran Bingham 
593e89586aSKieran Bingham static int adv748x_afe_status(struct adv748x_afe *afe, u32 *signal,
603e89586aSKieran Bingham 			      v4l2_std_id *std)
613e89586aSKieran Bingham {
623e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
633e89586aSKieran Bingham 	int info;
643e89586aSKieran Bingham 
653e89586aSKieran Bingham 	/* Read status from reg 0x10 of SDP RO Map */
663e89586aSKieran Bingham 	info = adv748x_afe_read_ro_map(state, ADV748X_SDP_RO_10);
673e89586aSKieran Bingham 	if (info < 0)
683e89586aSKieran Bingham 		return info;
693e89586aSKieran Bingham 
703e89586aSKieran Bingham 	if (signal)
713e89586aSKieran Bingham 		*signal = info & ADV748X_SDP_RO_10_IN_LOCK ?
723e89586aSKieran Bingham 				0 : V4L2_IN_ST_NO_SIGNAL;
733e89586aSKieran Bingham 
743e89586aSKieran Bingham 	if (!std)
753e89586aSKieran Bingham 		return 0;
763e89586aSKieran Bingham 
773e89586aSKieran Bingham 	/* Standard not valid if there is no signal */
783e89586aSKieran Bingham 	if (!(info & ADV748X_SDP_RO_10_IN_LOCK)) {
793e89586aSKieran Bingham 		*std = V4L2_STD_UNKNOWN;
803e89586aSKieran Bingham 		return 0;
813e89586aSKieran Bingham 	}
823e89586aSKieran Bingham 
833e89586aSKieran Bingham 	switch (info & 0x70) {
843e89586aSKieran Bingham 	case 0x00:
853e89586aSKieran Bingham 		*std = V4L2_STD_NTSC;
863e89586aSKieran Bingham 		break;
873e89586aSKieran Bingham 	case 0x10:
883e89586aSKieran Bingham 		*std = V4L2_STD_NTSC_443;
893e89586aSKieran Bingham 		break;
903e89586aSKieran Bingham 	case 0x20:
913e89586aSKieran Bingham 		*std = V4L2_STD_PAL_M;
923e89586aSKieran Bingham 		break;
933e89586aSKieran Bingham 	case 0x30:
943e89586aSKieran Bingham 		*std = V4L2_STD_PAL_60;
953e89586aSKieran Bingham 		break;
963e89586aSKieran Bingham 	case 0x40:
973e89586aSKieran Bingham 		*std = V4L2_STD_PAL;
983e89586aSKieran Bingham 		break;
993e89586aSKieran Bingham 	case 0x50:
1003e89586aSKieran Bingham 		*std = V4L2_STD_SECAM;
1013e89586aSKieran Bingham 		break;
1023e89586aSKieran Bingham 	case 0x60:
1033e89586aSKieran Bingham 		*std = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
1043e89586aSKieran Bingham 		break;
1053e89586aSKieran Bingham 	case 0x70:
1063e89586aSKieran Bingham 		*std = V4L2_STD_SECAM;
1073e89586aSKieran Bingham 		break;
1083e89586aSKieran Bingham 	default:
1093e89586aSKieran Bingham 		*std = V4L2_STD_UNKNOWN;
1103e89586aSKieran Bingham 		break;
1113e89586aSKieran Bingham 	}
1123e89586aSKieran Bingham 
1133e89586aSKieran Bingham 	return 0;
1143e89586aSKieran Bingham }
1153e89586aSKieran Bingham 
1163e89586aSKieran Bingham static void adv748x_afe_fill_format(struct adv748x_afe *afe,
1173e89586aSKieran Bingham 				    struct v4l2_mbus_framefmt *fmt)
1183e89586aSKieran Bingham {
1193e89586aSKieran Bingham 	memset(fmt, 0, sizeof(*fmt));
1203e89586aSKieran Bingham 
1213e89586aSKieran Bingham 	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
1223e89586aSKieran Bingham 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
1233e89586aSKieran Bingham 	fmt->field = V4L2_FIELD_ALTERNATE;
1243e89586aSKieran Bingham 
1253e89586aSKieran Bingham 	fmt->width = 720;
1263e89586aSKieran Bingham 	fmt->height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576;
1273e89586aSKieran Bingham 
1283e89586aSKieran Bingham 	/* Field height */
1293e89586aSKieran Bingham 	fmt->height /= 2;
1303e89586aSKieran Bingham }
1313e89586aSKieran Bingham 
1323e89586aSKieran Bingham static int adv748x_afe_std(v4l2_std_id std)
1333e89586aSKieran Bingham {
1343e89586aSKieran Bingham 	if (std == V4L2_STD_PAL_60)
1353e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL60;
1363e89586aSKieran Bingham 	if (std == V4L2_STD_NTSC_443)
1373e89586aSKieran Bingham 		return ADV748X_AFE_STD_NTSC_443;
1383e89586aSKieran Bingham 	if (std == V4L2_STD_PAL_N)
1393e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL_N;
1403e89586aSKieran Bingham 	if (std == V4L2_STD_PAL_M)
1413e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL_M;
1423e89586aSKieran Bingham 	if (std == V4L2_STD_PAL_Nc)
1433e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL_COMB_N;
1443e89586aSKieran Bingham 	if (std & V4L2_STD_NTSC)
1453e89586aSKieran Bingham 		return ADV748X_AFE_STD_NTSC_M;
1463e89586aSKieran Bingham 	if (std & V4L2_STD_PAL)
1473e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL_BG;
1483e89586aSKieran Bingham 	if (std & V4L2_STD_SECAM)
1493e89586aSKieran Bingham 		return ADV748X_AFE_STD_PAL_SECAM;
1503e89586aSKieran Bingham 
1513e89586aSKieran Bingham 	return -EINVAL;
1523e89586aSKieran Bingham }
1533e89586aSKieran Bingham 
1543e89586aSKieran Bingham static void adv748x_afe_set_video_standard(struct adv748x_state *state,
1553e89586aSKieran Bingham 					  int sdpstd)
1563e89586aSKieran Bingham {
1573e89586aSKieran Bingham 	sdp_clrset(state, ADV748X_SDP_VID_SEL, ADV748X_SDP_VID_SEL_MASK,
1583e89586aSKieran Bingham 		   (sdpstd & 0xf) << ADV748X_SDP_VID_SEL_SHIFT);
1593e89586aSKieran Bingham }
1603e89586aSKieran Bingham 
1613e89586aSKieran Bingham static int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input)
1623e89586aSKieran Bingham {
1633e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
1643e89586aSKieran Bingham 
1653e89586aSKieran Bingham 	return sdp_write(state, ADV748X_SDP_INSEL, input);
1663e89586aSKieran Bingham }
1673e89586aSKieran Bingham 
1683e89586aSKieran Bingham static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd,
1693e89586aSKieran Bingham 				     struct v4l2_fract *aspect)
1703e89586aSKieran Bingham {
1713e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
1723e89586aSKieran Bingham 
1733e89586aSKieran Bingham 	if (afe->curr_norm & V4L2_STD_525_60) {
1743e89586aSKieran Bingham 		aspect->numerator = 11;
1753e89586aSKieran Bingham 		aspect->denominator = 10;
1763e89586aSKieran Bingham 	} else {
1773e89586aSKieran Bingham 		aspect->numerator = 54;
1783e89586aSKieran Bingham 		aspect->denominator = 59;
1793e89586aSKieran Bingham 	}
1803e89586aSKieran Bingham 
1813e89586aSKieran Bingham 	return 0;
1823e89586aSKieran Bingham }
1833e89586aSKieran Bingham 
1843e89586aSKieran Bingham /* -----------------------------------------------------------------------------
1853e89586aSKieran Bingham  * v4l2_subdev_video_ops
1863e89586aSKieran Bingham  */
1873e89586aSKieran Bingham 
1883e89586aSKieran Bingham static int adv748x_afe_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
1893e89586aSKieran Bingham {
1903e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
1913e89586aSKieran Bingham 
1923e89586aSKieran Bingham 	*norm = afe->curr_norm;
1933e89586aSKieran Bingham 
1943e89586aSKieran Bingham 	return 0;
1953e89586aSKieran Bingham }
1963e89586aSKieran Bingham 
1973e89586aSKieran Bingham static int adv748x_afe_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
1983e89586aSKieran Bingham {
1993e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
2003e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
2013e89586aSKieran Bingham 	int afe_std = adv748x_afe_std(std);
2023e89586aSKieran Bingham 
2033e89586aSKieran Bingham 	if (afe_std < 0)
2043e89586aSKieran Bingham 		return afe_std;
2053e89586aSKieran Bingham 
2063e89586aSKieran Bingham 	mutex_lock(&state->mutex);
2073e89586aSKieran Bingham 
2083e89586aSKieran Bingham 	adv748x_afe_set_video_standard(state, afe_std);
2093e89586aSKieran Bingham 	afe->curr_norm = std;
2103e89586aSKieran Bingham 
2113e89586aSKieran Bingham 	mutex_unlock(&state->mutex);
2123e89586aSKieran Bingham 
2133e89586aSKieran Bingham 	return 0;
2143e89586aSKieran Bingham }
2153e89586aSKieran Bingham 
2163e89586aSKieran Bingham static int adv748x_afe_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
2173e89586aSKieran Bingham {
2183e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
2193e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
2203e89586aSKieran Bingham 	int ret;
2213e89586aSKieran Bingham 
2223e89586aSKieran Bingham 	mutex_lock(&state->mutex);
2233e89586aSKieran Bingham 
2243e89586aSKieran Bingham 	if (afe->streaming) {
2253e89586aSKieran Bingham 		ret = -EBUSY;
2263e89586aSKieran Bingham 		goto unlock;
2273e89586aSKieran Bingham 	}
2283e89586aSKieran Bingham 
2293e89586aSKieran Bingham 	/* Set auto detect mode */
2303e89586aSKieran Bingham 	adv748x_afe_set_video_standard(state,
2313e89586aSKieran Bingham 				       ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM);
2323e89586aSKieran Bingham 
2333e89586aSKieran Bingham 	msleep(100);
2343e89586aSKieran Bingham 
2353e89586aSKieran Bingham 	/* Read detected standard */
2363e89586aSKieran Bingham 	ret = adv748x_afe_status(afe, NULL, std);
2373e89586aSKieran Bingham 
2383e89586aSKieran Bingham 	/* Restore original state */
2393e89586aSKieran Bingham 	adv748x_afe_set_video_standard(state, afe->curr_norm);
2403e89586aSKieran Bingham 
2413e89586aSKieran Bingham unlock:
2423e89586aSKieran Bingham 	mutex_unlock(&state->mutex);
2433e89586aSKieran Bingham 
2443e89586aSKieran Bingham 	return ret;
2453e89586aSKieran Bingham }
2463e89586aSKieran Bingham 
2473e89586aSKieran Bingham static int adv748x_afe_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
2483e89586aSKieran Bingham {
2493e89586aSKieran Bingham 	*norm = V4L2_STD_ALL;
2503e89586aSKieran Bingham 
2513e89586aSKieran Bingham 	return 0;
2523e89586aSKieran Bingham }
2533e89586aSKieran Bingham 
2543e89586aSKieran Bingham static int adv748x_afe_g_input_status(struct v4l2_subdev *sd, u32 *status)
2553e89586aSKieran Bingham {
2563e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
2573e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
2583e89586aSKieran Bingham 	int ret;
2593e89586aSKieran Bingham 
2603e89586aSKieran Bingham 	mutex_lock(&state->mutex);
2613e89586aSKieran Bingham 
2623e89586aSKieran Bingham 	ret = adv748x_afe_status(afe, status, NULL);
2633e89586aSKieran Bingham 
2643e89586aSKieran Bingham 	mutex_unlock(&state->mutex);
2653e89586aSKieran Bingham 	return ret;
2663e89586aSKieran Bingham }
2673e89586aSKieran Bingham 
2683e89586aSKieran Bingham static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
2693e89586aSKieran Bingham {
2703e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
2713e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
2723e89586aSKieran Bingham 	int ret, signal = V4L2_IN_ST_NO_SIGNAL;
2733e89586aSKieran Bingham 
2743e89586aSKieran Bingham 	mutex_lock(&state->mutex);
2753e89586aSKieran Bingham 
2763e89586aSKieran Bingham 	if (enable) {
2773e89586aSKieran Bingham 		ret = adv748x_afe_s_input(afe, afe->input);
2783e89586aSKieran Bingham 		if (ret)
2793e89586aSKieran Bingham 			goto unlock;
2803e89586aSKieran Bingham 	}
2813e89586aSKieran Bingham 
2823e89586aSKieran Bingham 	ret = adv748x_txb_power(state, enable);
2833e89586aSKieran Bingham 	if (ret)
2843e89586aSKieran Bingham 		goto unlock;
2853e89586aSKieran Bingham 
2863e89586aSKieran Bingham 	afe->streaming = enable;
2873e89586aSKieran Bingham 
2883e89586aSKieran Bingham 	adv748x_afe_status(afe, &signal, NULL);
2893e89586aSKieran Bingham 	if (signal != V4L2_IN_ST_NO_SIGNAL)
2903e89586aSKieran Bingham 		adv_dbg(state, "Detected SDP signal\n");
2913e89586aSKieran Bingham 	else
2923e89586aSKieran Bingham 		adv_dbg(state, "Couldn't detect SDP video signal\n");
2933e89586aSKieran Bingham 
2943e89586aSKieran Bingham unlock:
2953e89586aSKieran Bingham 	mutex_unlock(&state->mutex);
2963e89586aSKieran Bingham 
2973e89586aSKieran Bingham 	return ret;
2983e89586aSKieran Bingham }
2993e89586aSKieran Bingham 
3003e89586aSKieran Bingham static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
3013e89586aSKieran Bingham 	.g_std = adv748x_afe_g_std,
3023e89586aSKieran Bingham 	.s_std = adv748x_afe_s_std,
3033e89586aSKieran Bingham 	.querystd = adv748x_afe_querystd,
3043e89586aSKieran Bingham 	.g_tvnorms = adv748x_afe_g_tvnorms,
3053e89586aSKieran Bingham 	.g_input_status = adv748x_afe_g_input_status,
3063e89586aSKieran Bingham 	.s_stream = adv748x_afe_s_stream,
3073e89586aSKieran Bingham 	.g_pixelaspect = adv748x_afe_g_pixelaspect,
3083e89586aSKieran Bingham };
3093e89586aSKieran Bingham 
3103e89586aSKieran Bingham /* -----------------------------------------------------------------------------
3113e89586aSKieran Bingham  * v4l2_subdev_pad_ops
3123e89586aSKieran Bingham  */
3133e89586aSKieran Bingham 
3143e89586aSKieran Bingham static int adv748x_afe_propagate_pixelrate(struct adv748x_afe *afe)
3153e89586aSKieran Bingham {
3163e89586aSKieran Bingham 	struct v4l2_subdev *tx;
3173e89586aSKieran Bingham 	unsigned int width, height, fps;
3183e89586aSKieran Bingham 
3193e89586aSKieran Bingham 	tx = adv748x_get_remote_sd(&afe->pads[ADV748X_AFE_SOURCE]);
3203e89586aSKieran Bingham 	if (!tx)
3213e89586aSKieran Bingham 		return -ENOLINK;
3223e89586aSKieran Bingham 
3233e89586aSKieran Bingham 	width = 720;
3243e89586aSKieran Bingham 	height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576;
3253e89586aSKieran Bingham 	fps = afe->curr_norm & V4L2_STD_525_60 ? 30 : 25;
3263e89586aSKieran Bingham 
3273e89586aSKieran Bingham 	return adv748x_csi2_set_pixelrate(tx, width * height * fps);
3283e89586aSKieran Bingham }
3293e89586aSKieran Bingham 
3303e89586aSKieran Bingham static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd,
3313e89586aSKieran Bingham 				      struct v4l2_subdev_pad_config *cfg,
3323e89586aSKieran Bingham 				      struct v4l2_subdev_mbus_code_enum *code)
3333e89586aSKieran Bingham {
3343e89586aSKieran Bingham 	if (code->index != 0)
3353e89586aSKieran Bingham 		return -EINVAL;
3363e89586aSKieran Bingham 
3373e89586aSKieran Bingham 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
3383e89586aSKieran Bingham 
3393e89586aSKieran Bingham 	return 0;
3403e89586aSKieran Bingham }
3413e89586aSKieran Bingham 
3423e89586aSKieran Bingham static int adv748x_afe_get_format(struct v4l2_subdev *sd,
3433e89586aSKieran Bingham 				      struct v4l2_subdev_pad_config *cfg,
3443e89586aSKieran Bingham 				      struct v4l2_subdev_format *sdformat)
3453e89586aSKieran Bingham {
3463e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
3473e89586aSKieran Bingham 	struct v4l2_mbus_framefmt *mbusformat;
3483e89586aSKieran Bingham 
3493e89586aSKieran Bingham 	/* It makes no sense to get the format of the analog sink pads */
3503e89586aSKieran Bingham 	if (sdformat->pad != ADV748X_AFE_SOURCE)
3513e89586aSKieran Bingham 		return -EINVAL;
3523e89586aSKieran Bingham 
3533e89586aSKieran Bingham 	if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
3543e89586aSKieran Bingham 		mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
3553e89586aSKieran Bingham 		sdformat->format = *mbusformat;
3563e89586aSKieran Bingham 	} else {
3573e89586aSKieran Bingham 		adv748x_afe_fill_format(afe, &sdformat->format);
3583e89586aSKieran Bingham 		adv748x_afe_propagate_pixelrate(afe);
3593e89586aSKieran Bingham 	}
3603e89586aSKieran Bingham 
3613e89586aSKieran Bingham 	return 0;
3623e89586aSKieran Bingham }
3633e89586aSKieran Bingham 
3643e89586aSKieran Bingham static int adv748x_afe_set_format(struct v4l2_subdev *sd,
3653e89586aSKieran Bingham 				      struct v4l2_subdev_pad_config *cfg,
3663e89586aSKieran Bingham 				      struct v4l2_subdev_format *sdformat)
3673e89586aSKieran Bingham {
3683e89586aSKieran Bingham 	struct v4l2_mbus_framefmt *mbusformat;
3693e89586aSKieran Bingham 
3703e89586aSKieran Bingham 	/* It makes no sense to get the format of the analog sink pads */
3713e89586aSKieran Bingham 	if (sdformat->pad != ADV748X_AFE_SOURCE)
3723e89586aSKieran Bingham 		return -EINVAL;
3733e89586aSKieran Bingham 
3743e89586aSKieran Bingham 	if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
3753e89586aSKieran Bingham 		return adv748x_afe_get_format(sd, cfg, sdformat);
3763e89586aSKieran Bingham 
3773e89586aSKieran Bingham 	mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
3783e89586aSKieran Bingham 	*mbusformat = sdformat->format;
3793e89586aSKieran Bingham 
3803e89586aSKieran Bingham 	return 0;
3813e89586aSKieran Bingham }
3823e89586aSKieran Bingham 
3833e89586aSKieran Bingham static const struct v4l2_subdev_pad_ops adv748x_afe_pad_ops = {
3843e89586aSKieran Bingham 	.enum_mbus_code = adv748x_afe_enum_mbus_code,
3853e89586aSKieran Bingham 	.set_fmt = adv748x_afe_set_format,
3863e89586aSKieran Bingham 	.get_fmt = adv748x_afe_get_format,
3873e89586aSKieran Bingham };
3883e89586aSKieran Bingham 
3893e89586aSKieran Bingham /* -----------------------------------------------------------------------------
3903e89586aSKieran Bingham  * v4l2_subdev_ops
3913e89586aSKieran Bingham  */
3923e89586aSKieran Bingham 
3933e89586aSKieran Bingham static const struct v4l2_subdev_ops adv748x_afe_ops = {
3943e89586aSKieran Bingham 	.video = &adv748x_afe_video_ops,
3953e89586aSKieran Bingham 	.pad = &adv748x_afe_pad_ops,
3963e89586aSKieran Bingham };
3973e89586aSKieran Bingham 
3983e89586aSKieran Bingham /* -----------------------------------------------------------------------------
3993e89586aSKieran Bingham  * Controls
4003e89586aSKieran Bingham  */
4013e89586aSKieran Bingham 
4023e89586aSKieran Bingham static const char * const afe_ctrl_frp_menu[] = {
4033e89586aSKieran Bingham 	"Disabled",
4043e89586aSKieran Bingham 	"Solid Blue",
4053e89586aSKieran Bingham 	"Color Bars",
4063e89586aSKieran Bingham 	"Grey Ramp",
4073e89586aSKieran Bingham 	"Cb Ramp",
4083e89586aSKieran Bingham 	"Cr Ramp",
4093e89586aSKieran Bingham 	"Boundary"
4103e89586aSKieran Bingham };
4113e89586aSKieran Bingham 
4123e89586aSKieran Bingham static int adv748x_afe_s_ctrl(struct v4l2_ctrl *ctrl)
4133e89586aSKieran Bingham {
4143e89586aSKieran Bingham 	struct adv748x_afe *afe = adv748x_ctrl_to_afe(ctrl);
4153e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
4163e89586aSKieran Bingham 	bool enable;
4173e89586aSKieran Bingham 	int ret;
4183e89586aSKieran Bingham 
4193e89586aSKieran Bingham 	ret = sdp_write(state, 0x0e, 0x00);
4203e89586aSKieran Bingham 	if (ret < 0)
4213e89586aSKieran Bingham 		return ret;
4223e89586aSKieran Bingham 
4233e89586aSKieran Bingham 	switch (ctrl->id) {
4243e89586aSKieran Bingham 	case V4L2_CID_BRIGHTNESS:
4253e89586aSKieran Bingham 		ret = sdp_write(state, ADV748X_SDP_BRI, ctrl->val);
4263e89586aSKieran Bingham 		break;
4273e89586aSKieran Bingham 	case V4L2_CID_HUE:
4283e89586aSKieran Bingham 		/* Hue is inverted according to HSL chart */
4293e89586aSKieran Bingham 		ret = sdp_write(state, ADV748X_SDP_HUE, -ctrl->val);
4303e89586aSKieran Bingham 		break;
4313e89586aSKieran Bingham 	case V4L2_CID_CONTRAST:
4323e89586aSKieran Bingham 		ret = sdp_write(state, ADV748X_SDP_CON, ctrl->val);
4333e89586aSKieran Bingham 		break;
4343e89586aSKieran Bingham 	case V4L2_CID_SATURATION:
4353e89586aSKieran Bingham 		ret = sdp_write(state, ADV748X_SDP_SD_SAT_U, ctrl->val);
4363e89586aSKieran Bingham 		if (ret)
4373e89586aSKieran Bingham 			break;
4383e89586aSKieran Bingham 		ret = sdp_write(state, ADV748X_SDP_SD_SAT_V, ctrl->val);
4393e89586aSKieran Bingham 		break;
4403e89586aSKieran Bingham 	case V4L2_CID_TEST_PATTERN:
4413e89586aSKieran Bingham 		enable = !!ctrl->val;
4423e89586aSKieran Bingham 
4433e89586aSKieran Bingham 		/* Enable/Disable Color bar test patterns */
4443e89586aSKieran Bingham 		ret = sdp_clrset(state, ADV748X_SDP_DEF, ADV748X_SDP_DEF_VAL_EN,
4453e89586aSKieran Bingham 				enable);
4463e89586aSKieran Bingham 		if (ret)
4473e89586aSKieran Bingham 			break;
4483e89586aSKieran Bingham 		ret = sdp_clrset(state, ADV748X_SDP_FRP, ADV748X_SDP_FRP_MASK,
4493e89586aSKieran Bingham 				enable ? ctrl->val - 1 : 0);
4503e89586aSKieran Bingham 		break;
4513e89586aSKieran Bingham 	default:
4523e89586aSKieran Bingham 		return -EINVAL;
4533e89586aSKieran Bingham 	}
4543e89586aSKieran Bingham 
4553e89586aSKieran Bingham 	return ret;
4563e89586aSKieran Bingham }
4573e89586aSKieran Bingham 
4583e89586aSKieran Bingham static const struct v4l2_ctrl_ops adv748x_afe_ctrl_ops = {
4593e89586aSKieran Bingham 	.s_ctrl = adv748x_afe_s_ctrl,
4603e89586aSKieran Bingham };
4613e89586aSKieran Bingham 
4623e89586aSKieran Bingham static int adv748x_afe_init_controls(struct adv748x_afe *afe)
4633e89586aSKieran Bingham {
4643e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
4653e89586aSKieran Bingham 
4663e89586aSKieran Bingham 	v4l2_ctrl_handler_init(&afe->ctrl_hdl, 5);
4673e89586aSKieran Bingham 
4683e89586aSKieran Bingham 	/* Use our mutex for the controls */
4693e89586aSKieran Bingham 	afe->ctrl_hdl.lock = &state->mutex;
4703e89586aSKieran Bingham 
4713e89586aSKieran Bingham 	v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
4723e89586aSKieran Bingham 			  V4L2_CID_BRIGHTNESS, ADV748X_SDP_BRI_MIN,
4733e89586aSKieran Bingham 			  ADV748X_SDP_BRI_MAX, 1, ADV748X_SDP_BRI_DEF);
4743e89586aSKieran Bingham 	v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
4753e89586aSKieran Bingham 			  V4L2_CID_CONTRAST, ADV748X_SDP_CON_MIN,
4763e89586aSKieran Bingham 			  ADV748X_SDP_CON_MAX, 1, ADV748X_SDP_CON_DEF);
4773e89586aSKieran Bingham 	v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
4783e89586aSKieran Bingham 			  V4L2_CID_SATURATION, ADV748X_SDP_SAT_MIN,
4793e89586aSKieran Bingham 			  ADV748X_SDP_SAT_MAX, 1, ADV748X_SDP_SAT_DEF);
4803e89586aSKieran Bingham 	v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
4813e89586aSKieran Bingham 			  V4L2_CID_HUE, ADV748X_SDP_HUE_MIN,
4823e89586aSKieran Bingham 			  ADV748X_SDP_HUE_MAX, 1, ADV748X_SDP_HUE_DEF);
4833e89586aSKieran Bingham 
4843e89586aSKieran Bingham 	v4l2_ctrl_new_std_menu_items(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
4853e89586aSKieran Bingham 				     V4L2_CID_TEST_PATTERN,
4863e89586aSKieran Bingham 				     ARRAY_SIZE(afe_ctrl_frp_menu) - 1,
4873e89586aSKieran Bingham 				     0, 0, afe_ctrl_frp_menu);
4883e89586aSKieran Bingham 
4893e89586aSKieran Bingham 	afe->sd.ctrl_handler = &afe->ctrl_hdl;
4903e89586aSKieran Bingham 	if (afe->ctrl_hdl.error) {
4913e89586aSKieran Bingham 		v4l2_ctrl_handler_free(&afe->ctrl_hdl);
4923e89586aSKieran Bingham 		return afe->ctrl_hdl.error;
4933e89586aSKieran Bingham 	}
4943e89586aSKieran Bingham 
4953e89586aSKieran Bingham 	return v4l2_ctrl_handler_setup(&afe->ctrl_hdl);
4963e89586aSKieran Bingham }
4973e89586aSKieran Bingham 
4983e89586aSKieran Bingham int adv748x_afe_init(struct adv748x_afe *afe)
4993e89586aSKieran Bingham {
5003e89586aSKieran Bingham 	struct adv748x_state *state = adv748x_afe_to_state(afe);
5013e89586aSKieran Bingham 	int ret;
5023e89586aSKieran Bingham 	unsigned int i;
5033e89586aSKieran Bingham 
5043e89586aSKieran Bingham 	afe->input = 0;
5053e89586aSKieran Bingham 	afe->streaming = false;
5063e89586aSKieran Bingham 	afe->curr_norm = V4L2_STD_NTSC_M;
5073e89586aSKieran Bingham 
5083e89586aSKieran Bingham 	adv748x_subdev_init(&afe->sd, state, &adv748x_afe_ops,
5093e89586aSKieran Bingham 			    MEDIA_ENT_F_ATV_DECODER, "afe");
5103e89586aSKieran Bingham 
5113e89586aSKieran Bingham 	/* Identify the first connector found as a default input if set */
5123e89586aSKieran Bingham 	for (i = ADV748X_PORT_AIN0; i <= ADV748X_PORT_AIN7; i++) {
5133e89586aSKieran Bingham 		/* Inputs and ports are 1-indexed to match the data sheet */
5143e89586aSKieran Bingham 		if (state->endpoints[i]) {
5153e89586aSKieran Bingham 			afe->input = i;
5163e89586aSKieran Bingham 			break;
5173e89586aSKieran Bingham 		}
5183e89586aSKieran Bingham 	}
5193e89586aSKieran Bingham 
5203e89586aSKieran Bingham 	adv748x_afe_s_input(afe, afe->input);
5213e89586aSKieran Bingham 
5223e89586aSKieran Bingham 	adv_dbg(state, "AFE Default input set to %d\n", afe->input);
5233e89586aSKieran Bingham 
5243e89586aSKieran Bingham 	/* Entity pads and sinks are 0-indexed to match the pads */
5253e89586aSKieran Bingham 	for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++)
5263e89586aSKieran Bingham 		afe->pads[i].flags = MEDIA_PAD_FL_SINK;
5273e89586aSKieran Bingham 
5283e89586aSKieran Bingham 	afe->pads[ADV748X_AFE_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
5293e89586aSKieran Bingham 
5303e89586aSKieran Bingham 	ret = media_entity_pads_init(&afe->sd.entity, ADV748X_AFE_NR_PADS,
5313e89586aSKieran Bingham 			afe->pads);
5323e89586aSKieran Bingham 	if (ret)
5333e89586aSKieran Bingham 		return ret;
5343e89586aSKieran Bingham 
5353e89586aSKieran Bingham 	ret = adv748x_afe_init_controls(afe);
5363e89586aSKieran Bingham 	if (ret)
5373e89586aSKieran Bingham 		goto error;
5383e89586aSKieran Bingham 
5393e89586aSKieran Bingham 	return 0;
5403e89586aSKieran Bingham 
5413e89586aSKieran Bingham error:
5423e89586aSKieran Bingham 	media_entity_cleanup(&afe->sd.entity);
5433e89586aSKieran Bingham 
5443e89586aSKieran Bingham 	return ret;
5453e89586aSKieran Bingham }
5463e89586aSKieran Bingham 
5473e89586aSKieran Bingham void adv748x_afe_cleanup(struct adv748x_afe *afe)
5483e89586aSKieran Bingham {
5493e89586aSKieran Bingham 	v4l2_device_unregister_subdev(&afe->sd);
5503e89586aSKieran Bingham 	media_entity_cleanup(&afe->sd.entity);
5513e89586aSKieran Bingham 	v4l2_ctrl_handler_free(&afe->ctrl_hdl);
5523e89586aSKieran Bingham }
553