123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Driver for Analog Devices ADV748X 8 channel analog front end (AFE) receiver
- * with standard definition processor (SDP)
- *
- * Copyright (C) 2017 Renesas Electronics Corp.
- */
- #include <linux/delay.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/v4l2-dv-timings.h>
- #include <media/v4l2-ctrls.h>
- #include <media/v4l2-device.h>
- #include <media/v4l2-dv-timings.h>
- #include <media/v4l2-ioctl.h>
- #include "adv748x.h"
- /* -----------------------------------------------------------------------------
- * SDP
- */
- #define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM 0x0
- #define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1
- #define ADV748X_AFE_STD_AD_PAL_N_NTSC_J_SECAM 0x2
- #define ADV748X_AFE_STD_AD_PAL_N_NTSC_M_SECAM 0x3
- #define ADV748X_AFE_STD_NTSC_J 0x4
- #define ADV748X_AFE_STD_NTSC_M 0x5
- #define ADV748X_AFE_STD_PAL60 0x6
- #define ADV748X_AFE_STD_NTSC_443 0x7
- #define ADV748X_AFE_STD_PAL_BG 0x8
- #define ADV748X_AFE_STD_PAL_N 0x9
- #define ADV748X_AFE_STD_PAL_M 0xa
- #define ADV748X_AFE_STD_PAL_M_PED 0xb
- #define ADV748X_AFE_STD_PAL_COMB_N 0xc
- #define ADV748X_AFE_STD_PAL_COMB_N_PED 0xd
- #define ADV748X_AFE_STD_PAL_SECAM 0xe
- #define ADV748X_AFE_STD_PAL_SECAM_PED 0xf
- static int adv748x_afe_read_ro_map(struct adv748x_state *state, u8 reg)
- {
- int ret;
- /* Select SDP Read-Only Main Map */
- ret = sdp_write(state, ADV748X_SDP_MAP_SEL,
- ADV748X_SDP_MAP_SEL_RO_MAIN);
- if (ret < 0)
- return ret;
- return sdp_read(state, reg);
- }
- static int adv748x_afe_status(struct adv748x_afe *afe, u32 *signal,
- v4l2_std_id *std)
- {
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- int info;
- /* Read status from reg 0x10 of SDP RO Map */
- info = adv748x_afe_read_ro_map(state, ADV748X_SDP_RO_10);
- if (info < 0)
- return info;
- if (signal)
- *signal = info & ADV748X_SDP_RO_10_IN_LOCK ?
- 0 : V4L2_IN_ST_NO_SIGNAL;
- if (!std)
- return 0;
- /* Standard not valid if there is no signal */
- if (!(info & ADV748X_SDP_RO_10_IN_LOCK)) {
- *std = V4L2_STD_UNKNOWN;
- return 0;
- }
- switch (info & 0x70) {
- case 0x00:
- *std = V4L2_STD_NTSC;
- break;
- case 0x10:
- *std = V4L2_STD_NTSC_443;
- break;
- case 0x20:
- *std = V4L2_STD_PAL_M;
- break;
- case 0x30:
- *std = V4L2_STD_PAL_60;
- break;
- case 0x40:
- *std = V4L2_STD_PAL;
- break;
- case 0x50:
- *std = V4L2_STD_SECAM;
- break;
- case 0x60:
- *std = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
- break;
- case 0x70:
- *std = V4L2_STD_SECAM;
- break;
- default:
- *std = V4L2_STD_UNKNOWN;
- break;
- }
- return 0;
- }
- static void adv748x_afe_fill_format(struct adv748x_afe *afe,
- struct v4l2_mbus_framefmt *fmt)
- {
- memset(fmt, 0, sizeof(*fmt));
- fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->field = V4L2_FIELD_ALTERNATE;
- fmt->width = 720;
- fmt->height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576;
- /* Field height */
- fmt->height /= 2;
- }
- static int adv748x_afe_std(v4l2_std_id std)
- {
- if (std == V4L2_STD_PAL_60)
- return ADV748X_AFE_STD_PAL60;
- if (std == V4L2_STD_NTSC_443)
- return ADV748X_AFE_STD_NTSC_443;
- if (std == V4L2_STD_PAL_N)
- return ADV748X_AFE_STD_PAL_N;
- if (std == V4L2_STD_PAL_M)
- return ADV748X_AFE_STD_PAL_M;
- if (std == V4L2_STD_PAL_Nc)
- return ADV748X_AFE_STD_PAL_COMB_N;
- if (std & V4L2_STD_NTSC)
- return ADV748X_AFE_STD_NTSC_M;
- if (std & V4L2_STD_PAL)
- return ADV748X_AFE_STD_PAL_BG;
- if (std & V4L2_STD_SECAM)
- return ADV748X_AFE_STD_PAL_SECAM;
- return -EINVAL;
- }
- static void adv748x_afe_set_video_standard(struct adv748x_state *state,
- int sdpstd)
- {
- sdp_clrset(state, ADV748X_SDP_VID_SEL, ADV748X_SDP_VID_SEL_MASK,
- (sdpstd & 0xf) << ADV748X_SDP_VID_SEL_SHIFT);
- }
- int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input)
- {
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- return sdp_write(state, ADV748X_SDP_INSEL, input);
- }
- static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd,
- struct v4l2_fract *aspect)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- if (afe->curr_norm & V4L2_STD_525_60) {
- aspect->numerator = 11;
- aspect->denominator = 10;
- } else {
- aspect->numerator = 54;
- aspect->denominator = 59;
- }
- return 0;
- }
- /* -----------------------------------------------------------------------------
- * v4l2_subdev_video_ops
- */
- static int adv748x_afe_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- *norm = afe->curr_norm;
- return 0;
- }
- static int adv748x_afe_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- int afe_std = adv748x_afe_std(std);
- if (afe_std < 0)
- return afe_std;
- mutex_lock(&state->mutex);
- adv748x_afe_set_video_standard(state, afe_std);
- afe->curr_norm = std;
- mutex_unlock(&state->mutex);
- return 0;
- }
- static int adv748x_afe_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- int afe_std;
- int ret;
- mutex_lock(&state->mutex);
- if (afe->streaming) {
- ret = -EBUSY;
- goto unlock;
- }
- /* Set auto detect mode */
- adv748x_afe_set_video_standard(state,
- ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM);
- msleep(100);
- /* Read detected standard */
- ret = adv748x_afe_status(afe, NULL, std);
- afe_std = adv748x_afe_std(afe->curr_norm);
- if (afe_std < 0)
- goto unlock;
- /* Restore original state */
- adv748x_afe_set_video_standard(state, afe_std);
- unlock:
- mutex_unlock(&state->mutex);
- return ret;
- }
- static int adv748x_afe_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
- {
- *norm = V4L2_STD_ALL;
- return 0;
- }
- static int adv748x_afe_g_input_status(struct v4l2_subdev *sd, u32 *status)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- int ret;
- mutex_lock(&state->mutex);
- ret = adv748x_afe_status(afe, status, NULL);
- mutex_unlock(&state->mutex);
- return ret;
- }
- static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- u32 signal = V4L2_IN_ST_NO_SIGNAL;
- int ret;
- mutex_lock(&state->mutex);
- if (enable) {
- ret = adv748x_afe_s_input(afe, afe->input);
- if (ret)
- goto unlock;
- }
- ret = adv748x_tx_power(afe->tx, enable);
- if (ret)
- goto unlock;
- afe->streaming = enable;
- adv748x_afe_status(afe, &signal, NULL);
- if (signal != V4L2_IN_ST_NO_SIGNAL)
- adv_dbg(state, "Detected SDP signal\n");
- else
- adv_dbg(state, "Couldn't detect SDP video signal\n");
- unlock:
- mutex_unlock(&state->mutex);
- return ret;
- }
- static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
- .g_std = adv748x_afe_g_std,
- .s_std = adv748x_afe_s_std,
- .querystd = adv748x_afe_querystd,
- .g_tvnorms = adv748x_afe_g_tvnorms,
- .g_input_status = adv748x_afe_g_input_status,
- .s_stream = adv748x_afe_s_stream,
- .g_pixelaspect = adv748x_afe_g_pixelaspect,
- };
- /* -----------------------------------------------------------------------------
- * v4l2_subdev_pad_ops
- */
- static int adv748x_afe_propagate_pixelrate(struct adv748x_afe *afe)
- {
- struct v4l2_subdev *tx;
- tx = adv748x_get_remote_sd(&afe->pads[ADV748X_AFE_SOURCE]);
- if (!tx)
- return -ENOLINK;
- /*
- * The ADV748x ADC sampling frequency is twice the externally supplied
- * clock whose frequency is required to be 28.63636 MHz. It oversamples
- * with a factor of 4 resulting in a pixel rate of 14.3180180 MHz.
- */
- return adv748x_csi2_set_pixelrate(tx, 14318180);
- }
- static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
- {
- if (code->index != 0)
- return -EINVAL;
- code->code = MEDIA_BUS_FMT_UYVY8_2X8;
- return 0;
- }
- static int adv748x_afe_get_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat)
- {
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
- struct v4l2_mbus_framefmt *mbusformat;
- /* It makes no sense to get the format of the analog sink pads */
- if (sdformat->pad != ADV748X_AFE_SOURCE)
- return -EINVAL;
- if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
- mbusformat = v4l2_subdev_get_try_format(sd, sd_state,
- sdformat->pad);
- sdformat->format = *mbusformat;
- } else {
- adv748x_afe_fill_format(afe, &sdformat->format);
- adv748x_afe_propagate_pixelrate(afe);
- }
- return 0;
- }
- static int adv748x_afe_set_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat)
- {
- struct v4l2_mbus_framefmt *mbusformat;
- /* It makes no sense to get the format of the analog sink pads */
- if (sdformat->pad != ADV748X_AFE_SOURCE)
- return -EINVAL;
- if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return adv748x_afe_get_format(sd, sd_state, sdformat);
- mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad);
- *mbusformat = sdformat->format;
- return 0;
- }
- static const struct v4l2_subdev_pad_ops adv748x_afe_pad_ops = {
- .enum_mbus_code = adv748x_afe_enum_mbus_code,
- .set_fmt = adv748x_afe_set_format,
- .get_fmt = adv748x_afe_get_format,
- };
- /* -----------------------------------------------------------------------------
- * v4l2_subdev_ops
- */
- static const struct v4l2_subdev_ops adv748x_afe_ops = {
- .video = &adv748x_afe_video_ops,
- .pad = &adv748x_afe_pad_ops,
- };
- /* -----------------------------------------------------------------------------
- * Controls
- */
- static const char * const afe_ctrl_frp_menu[] = {
- "Disabled",
- "Solid Blue",
- "Color Bars",
- "Grey Ramp",
- "Cb Ramp",
- "Cr Ramp",
- "Boundary"
- };
- static int adv748x_afe_s_ctrl(struct v4l2_ctrl *ctrl)
- {
- struct adv748x_afe *afe = adv748x_ctrl_to_afe(ctrl);
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- bool enable;
- int ret;
- ret = sdp_write(state, 0x0e, 0x00);
- if (ret < 0)
- return ret;
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ret = sdp_write(state, ADV748X_SDP_BRI, ctrl->val);
- break;
- case V4L2_CID_HUE:
- /* Hue is inverted according to HSL chart */
- ret = sdp_write(state, ADV748X_SDP_HUE, -ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- ret = sdp_write(state, ADV748X_SDP_CON, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- ret = sdp_write(state, ADV748X_SDP_SD_SAT_U, ctrl->val);
- if (ret)
- break;
- ret = sdp_write(state, ADV748X_SDP_SD_SAT_V, ctrl->val);
- break;
- case V4L2_CID_TEST_PATTERN:
- enable = !!ctrl->val;
- /* Enable/Disable Color bar test patterns */
- ret = sdp_clrset(state, ADV748X_SDP_DEF, ADV748X_SDP_DEF_VAL_EN,
- enable);
- if (ret)
- break;
- ret = sdp_clrset(state, ADV748X_SDP_FRP, ADV748X_SDP_FRP_MASK,
- enable ? ctrl->val - 1 : 0);
- break;
- default:
- return -EINVAL;
- }
- return ret;
- }
- static const struct v4l2_ctrl_ops adv748x_afe_ctrl_ops = {
- .s_ctrl = adv748x_afe_s_ctrl,
- };
- static int adv748x_afe_init_controls(struct adv748x_afe *afe)
- {
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- v4l2_ctrl_handler_init(&afe->ctrl_hdl, 5);
- /* Use our mutex for the controls */
- afe->ctrl_hdl.lock = &state->mutex;
- v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
- V4L2_CID_BRIGHTNESS, ADV748X_SDP_BRI_MIN,
- ADV748X_SDP_BRI_MAX, 1, ADV748X_SDP_BRI_DEF);
- v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
- V4L2_CID_CONTRAST, ADV748X_SDP_CON_MIN,
- ADV748X_SDP_CON_MAX, 1, ADV748X_SDP_CON_DEF);
- v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
- V4L2_CID_SATURATION, ADV748X_SDP_SAT_MIN,
- ADV748X_SDP_SAT_MAX, 1, ADV748X_SDP_SAT_DEF);
- v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
- V4L2_CID_HUE, ADV748X_SDP_HUE_MIN,
- ADV748X_SDP_HUE_MAX, 1, ADV748X_SDP_HUE_DEF);
- v4l2_ctrl_new_std_menu_items(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
- V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(afe_ctrl_frp_menu) - 1,
- 0, 0, afe_ctrl_frp_menu);
- afe->sd.ctrl_handler = &afe->ctrl_hdl;
- if (afe->ctrl_hdl.error) {
- v4l2_ctrl_handler_free(&afe->ctrl_hdl);
- return afe->ctrl_hdl.error;
- }
- return v4l2_ctrl_handler_setup(&afe->ctrl_hdl);
- }
- int adv748x_afe_init(struct adv748x_afe *afe)
- {
- struct adv748x_state *state = adv748x_afe_to_state(afe);
- int ret;
- unsigned int i;
- afe->input = 0;
- afe->streaming = false;
- afe->curr_norm = V4L2_STD_NTSC_M;
- adv748x_subdev_init(&afe->sd, state, &adv748x_afe_ops,
- MEDIA_ENT_F_ATV_DECODER, "afe");
- /* Identify the first connector found as a default input if set */
- for (i = ADV748X_PORT_AIN0; i <= ADV748X_PORT_AIN7; i++) {
- /* Inputs and ports are 1-indexed to match the data sheet */
- if (state->endpoints[i]) {
- afe->input = i;
- break;
- }
- }
- adv748x_afe_s_input(afe, afe->input);
- adv_dbg(state, "AFE Default input set to %d\n", afe->input);
- /* Entity pads and sinks are 0-indexed to match the pads */
- for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++)
- afe->pads[i].flags = MEDIA_PAD_FL_SINK;
- afe->pads[ADV748X_AFE_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&afe->sd.entity, ADV748X_AFE_NR_PADS,
- afe->pads);
- if (ret)
- return ret;
- ret = adv748x_afe_init_controls(afe);
- if (ret)
- goto error;
- return 0;
- error:
- media_entity_cleanup(&afe->sd.entity);
- return ret;
- }
- void adv748x_afe_cleanup(struct adv748x_afe *afe)
- {
- v4l2_device_unregister_subdev(&afe->sd);
- media_entity_cleanup(&afe->sd.entity);
- v4l2_ctrl_handler_free(&afe->ctrl_hdl);
- }
|