^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Driver for Analog Devices ADV748X HDMI receiver and Component Processor (CP)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2017 Renesas Electronics Corp.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <media/v4l2-ctrls.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <media/v4l2-device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <media/v4l2-dv-timings.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <media/v4l2-ioctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <uapi/linux/v4l2-dv-timings.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "adv748x.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) /* -----------------------------------------------------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * HDMI and CP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define ADV748X_HDMI_MIN_WIDTH 640
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define ADV748X_HDMI_MAX_WIDTH 1920
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define ADV748X_HDMI_MIN_HEIGHT 480
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define ADV748X_HDMI_MAX_HEIGHT 1200
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) /* V4L2_DV_BT_CEA_720X480I59_94 - 0.5 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define ADV748X_HDMI_MIN_PIXELCLOCK 13000000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* V4L2_DV_BT_DMT_1600X1200P60 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define ADV748X_HDMI_MAX_PIXELCLOCK 162000000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static const struct v4l2_dv_timings_cap adv748x_hdmi_timings_cap = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) .type = V4L2_DV_BT_656_1120,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /* keep this initialization for compatibility with GCC < 4.4.6 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) .reserved = { 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) V4L2_INIT_BT_TIMINGS(ADV748X_HDMI_MIN_WIDTH, ADV748X_HDMI_MAX_WIDTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) ADV748X_HDMI_MIN_HEIGHT, ADV748X_HDMI_MAX_HEIGHT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) ADV748X_HDMI_MIN_PIXELCLOCK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) ADV748X_HDMI_MAX_PIXELCLOCK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) V4L2_DV_BT_CAP_PROGRESSIVE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) struct adv748x_hdmi_video_standards {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct v4l2_dv_timings timings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) u8 vid_std;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) u8 v_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) static const struct adv748x_hdmi_video_standards
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) adv748x_hdmi_video_standards[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) { V4L2_DV_BT_CEA_720X480P59_94, 0x4a, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) { V4L2_DV_BT_CEA_720X576P50, 0x4b, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) { V4L2_DV_BT_CEA_1280X720P60, 0x53, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) { V4L2_DV_BT_CEA_1280X720P50, 0x53, 0x01 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) { V4L2_DV_BT_CEA_1280X720P30, 0x53, 0x02 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) { V4L2_DV_BT_CEA_1280X720P25, 0x53, 0x03 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) { V4L2_DV_BT_CEA_1280X720P24, 0x53, 0x04 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) { V4L2_DV_BT_CEA_1920X1080P60, 0x5e, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) { V4L2_DV_BT_CEA_1920X1080P50, 0x5e, 0x01 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) { V4L2_DV_BT_CEA_1920X1080P30, 0x5e, 0x02 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) { V4L2_DV_BT_CEA_1920X1080P25, 0x5e, 0x03 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) { V4L2_DV_BT_CEA_1920X1080P24, 0x5e, 0x04 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* SVGA */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) { V4L2_DV_BT_DMT_800X600P56, 0x80, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) { V4L2_DV_BT_DMT_800X600P60, 0x81, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) { V4L2_DV_BT_DMT_800X600P72, 0x82, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) { V4L2_DV_BT_DMT_800X600P75, 0x83, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) { V4L2_DV_BT_DMT_800X600P85, 0x84, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) /* SXGA */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) { V4L2_DV_BT_DMT_1280X1024P60, 0x85, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) { V4L2_DV_BT_DMT_1280X1024P75, 0x86, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) /* VGA */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) { V4L2_DV_BT_DMT_640X480P60, 0x88, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) { V4L2_DV_BT_DMT_640X480P72, 0x89, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) { V4L2_DV_BT_DMT_640X480P75, 0x8a, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) { V4L2_DV_BT_DMT_640X480P85, 0x8b, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) /* XGA */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) { V4L2_DV_BT_DMT_1024X768P60, 0x8c, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) { V4L2_DV_BT_DMT_1024X768P70, 0x8d, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) { V4L2_DV_BT_DMT_1024X768P75, 0x8e, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) { V4L2_DV_BT_DMT_1024X768P85, 0x8f, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) /* UXGA */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) { V4L2_DV_BT_DMT_1600X1200P60, 0x96, 0x00 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static void adv748x_hdmi_fill_format(struct adv748x_hdmi *hdmi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct v4l2_mbus_framefmt *fmt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) memset(fmt, 0, sizeof(*fmt));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) fmt->field = hdmi->timings.bt.interlaced ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) /* TODO: The colorspace depends on the AVI InfoFrame contents */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) fmt->colorspace = V4L2_COLORSPACE_SRGB;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) fmt->width = hdmi->timings.bt.width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) fmt->height = hdmi->timings.bt.height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (fmt->field == V4L2_FIELD_ALTERNATE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) fmt->height /= 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static void adv748x_fill_optional_dv_timings(struct v4l2_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) v4l2_find_dv_timings_cap(timings, &adv748x_hdmi_timings_cap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 250000, NULL, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static bool adv748x_hdmi_has_signal(struct adv748x_state *state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* Check that VERT_FILTER and DE_REGEN is locked */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) val = hdmi_read(state, ADV748X_HDMI_LW1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) return (val & ADV748X_HDMI_LW1_VERT_FILTER) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) (val & ADV748X_HDMI_LW1_DE_REGEN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) static int adv748x_hdmi_read_pixelclock(struct adv748x_state *state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) int a, b;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) a = hdmi_read(state, ADV748X_HDMI_TMDS_1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) b = hdmi_read(state, ADV748X_HDMI_TMDS_2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (a < 0 || b < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return -ENODATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) * The high 9 bits store TMDS frequency measurement in MHz
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) * The low 7 bits of TMDS_2 store the 7-bit TMDS fractional frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) * measurement in 1/128 MHz
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) * adv748x_hdmi_set_de_timings: Adjust horizontal picture offset through DE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) * HDMI CP uses a Data Enable synchronisation timing reference
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) * Vary the leading and trailing edge position of the DE signal output by the CP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) * core. Values are stored as signed-twos-complement in one-pixel-clock units
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) * The start and end are shifted equally by the 10-bit shift value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static void adv748x_hdmi_set_de_timings(struct adv748x_state *state, int shift)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) u8 high, low;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /* POS_HIGH stores bits 8 and 9 of both the start and end */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) high = ADV748X_CP_DE_POS_HIGH_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) high |= (shift & 0x300) >> 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) low = shift & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* The sequence of the writes is important and must be followed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) cp_write(state, ADV748X_CP_DE_POS_HIGH, high);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) cp_write(state, ADV748X_CP_DE_POS_END_LOW, low);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) high |= (shift & 0x300) >> 6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) cp_write(state, ADV748X_CP_DE_POS_HIGH, high);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) cp_write(state, ADV748X_CP_DE_POS_START_LOW, low);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) static int adv748x_hdmi_set_video_timings(struct adv748x_state *state,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) const struct v4l2_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) const struct adv748x_hdmi_video_standards *stds =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) adv748x_hdmi_video_standards;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) unsigned int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if (!v4l2_match_dv_timings(timings, &stds[i].timings, 250000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) false))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) * When setting cp_vid_std to either 720p, 1080i, or 1080p, the video
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) * will get shifted horizontally to the left in active video mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) * The de_h_start and de_h_end controls are used to centre the picture
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) * correctly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) switch (stds[i].vid_std) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) case 0x53: /* 720p */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) adv748x_hdmi_set_de_timings(state, -40);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) case 0x54: /* 1080i */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) case 0x5e: /* 1080p */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) adv748x_hdmi_set_de_timings(state, -44);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) adv748x_hdmi_set_de_timings(state, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) io_write(state, ADV748X_IO_VID_STD, stds[i].vid_std);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) io_clrset(state, ADV748X_IO_DATAPATH, ADV748X_IO_DATAPATH_VFREQ_M,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) stds[i].v_freq << ADV748X_IO_DATAPATH_VFREQ_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) /* -----------------------------------------------------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) * v4l2_subdev_video_ops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) struct v4l2_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (!timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (v4l2_match_dv_timings(&hdmi->timings, timings, 0, false))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (!v4l2_valid_dv_timings(timings, &adv748x_hdmi_timings_cap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) NULL, NULL))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) return -ERANGE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) adv748x_fill_optional_dv_timings(timings);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) mutex_lock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) ret = adv748x_hdmi_set_video_timings(state, timings);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) goto error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) hdmi->timings = *timings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) cp_clrset(state, ADV748X_CP_VID_ADJ_2, ADV748X_CP_VID_ADJ_2_INTERLACED,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) timings->bt.interlaced ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) ADV748X_CP_VID_ADJ_2_INTERLACED : 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) mutex_unlock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) mutex_unlock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) struct v4l2_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) mutex_lock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) *timings = hdmi->timings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) mutex_unlock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) struct v4l2_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) struct v4l2_bt_timings *bt = &timings->bt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) int pixelclock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) int polarity;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) if (!timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) memset(timings, 0, sizeof(struct v4l2_dv_timings));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) if (!adv748x_hdmi_has_signal(state))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) return -ENOLINK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) pixelclock = adv748x_hdmi_read_pixelclock(state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (pixelclock < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) return -ENODATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) timings->type = V4L2_DV_BT_656_1120;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) bt->pixelclock = pixelclock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) bt->interlaced = hdmi_read(state, ADV748X_HDMI_F1H1) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) ADV748X_HDMI_F1H1_INTERLACED ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) bt->width = hdmi_read16(state, ADV748X_HDMI_LW1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) ADV748X_HDMI_LW1_WIDTH_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) bt->height = hdmi_read16(state, ADV748X_HDMI_F0H1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) ADV748X_HDMI_F0H1_HEIGHT_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) bt->hfrontporch = hdmi_read16(state, ADV748X_HDMI_HFRONT_PORCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) ADV748X_HDMI_HFRONT_PORCH_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) bt->hsync = hdmi_read16(state, ADV748X_HDMI_HSYNC_WIDTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) ADV748X_HDMI_HSYNC_WIDTH_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) bt->hbackporch = hdmi_read16(state, ADV748X_HDMI_HBACK_PORCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) ADV748X_HDMI_HBACK_PORCH_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) bt->vfrontporch = hdmi_read16(state, ADV748X_HDMI_VFRONT_PORCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) ADV748X_HDMI_VFRONT_PORCH_MASK) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) bt->vsync = hdmi_read16(state, ADV748X_HDMI_VSYNC_WIDTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) ADV748X_HDMI_VSYNC_WIDTH_MASK) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) bt->vbackporch = hdmi_read16(state, ADV748X_HDMI_VBACK_PORCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) ADV748X_HDMI_VBACK_PORCH_MASK) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) polarity = hdmi_read(state, 0x05);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) bt->polarities = (polarity & BIT(4) ? V4L2_DV_VSYNC_POS_POL : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) (polarity & BIT(5) ? V4L2_DV_HSYNC_POS_POL : 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) if (bt->interlaced == V4L2_DV_INTERLACED) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) bt->height += hdmi_read16(state, 0x0b, 0x1fff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) bt->il_vfrontporch = hdmi_read16(state, 0x2c, 0x3fff) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) bt->il_vsync = hdmi_read16(state, 0x30, 0x3fff) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) bt->il_vbackporch = hdmi_read16(state, 0x34, 0x3fff) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) adv748x_fill_optional_dv_timings(timings);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) * No interrupt handling is implemented yet.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) * There should be an IRQ when a cable is plugged and the new timings
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) * should be figured out and stored to state.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) hdmi->timings = *timings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) static int adv748x_hdmi_g_input_status(struct v4l2_subdev *sd, u32 *status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) mutex_lock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) *status = adv748x_hdmi_has_signal(state) ? 0 : V4L2_IN_ST_NO_SIGNAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) mutex_unlock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) mutex_lock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) ret = adv748x_tx_power(hdmi->tx, enable);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) if (adv748x_hdmi_has_signal(state))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) adv_dbg(state, "Detected HDMI signal\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) adv_dbg(state, "Couldn't detect HDMI video signal\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) mutex_unlock(&state->mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) struct v4l2_fract *aspect)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) aspect->numerator = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) aspect->denominator = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) .s_dv_timings = adv748x_hdmi_s_dv_timings,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) .g_dv_timings = adv748x_hdmi_g_dv_timings,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) .query_dv_timings = adv748x_hdmi_query_dv_timings,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) .g_input_status = adv748x_hdmi_g_input_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) .s_stream = adv748x_hdmi_s_stream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) .g_pixelaspect = adv748x_hdmi_g_pixelaspect,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) /* -----------------------------------------------------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) * v4l2_subdev_pad_ops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) struct v4l2_subdev *tx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) struct v4l2_dv_timings timings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) tx = adv748x_get_remote_sd(&hdmi->pads[ADV748X_HDMI_SOURCE]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) if (!tx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) return -ENOLINK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) struct v4l2_subdev_pad_config *cfg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) struct v4l2_subdev_mbus_code_enum *code)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) if (code->index != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) code->code = MEDIA_BUS_FMT_RGB888_1X24;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) static int adv748x_hdmi_get_format(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) struct v4l2_subdev_pad_config *cfg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) struct v4l2_subdev_format *sdformat)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) struct v4l2_mbus_framefmt *mbusformat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) if (sdformat->pad != ADV748X_HDMI_SOURCE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) sdformat->format = *mbusformat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) adv748x_hdmi_fill_format(hdmi, &sdformat->format);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) adv748x_hdmi_propagate_pixelrate(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) static int adv748x_hdmi_set_format(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) struct v4l2_subdev_pad_config *cfg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) struct v4l2_subdev_format *sdformat)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) struct v4l2_mbus_framefmt *mbusformat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) if (sdformat->pad != ADV748X_HDMI_SOURCE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) return adv748x_hdmi_get_format(sd, cfg, sdformat);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) *mbusformat = sdformat->format;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) static int adv748x_hdmi_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) memset(edid->reserved, 0, sizeof(edid->reserved));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) if (!hdmi->edid.present)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) return -ENODATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) if (edid->start_block == 0 && edid->blocks == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) edid->blocks = hdmi->edid.blocks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) if (edid->start_block >= hdmi->edid.blocks)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) if (edid->start_block + edid->blocks > hdmi->edid.blocks)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) edid->blocks = hdmi->edid.blocks - edid->start_block;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) memcpy(edid->edid, hdmi->edid.edid + edid->start_block * 128,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) edid->blocks * 128);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) static inline int adv748x_hdmi_edid_write_block(struct adv748x_hdmi *hdmi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) unsigned int total_len, const u8 *val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) int len = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) adv_dbg(state, "%s: write EDID block (%d byte)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) __func__, total_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) while (!err && i < total_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) I2C_SMBUS_BLOCK_MAX :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) (total_len - i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) err = adv748x_write_block(state, ADV748X_PAGE_EDID,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) i, val + i, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) i += len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) static int adv748x_hdmi_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) memset(edid->reserved, 0, sizeof(edid->reserved));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) if (edid->start_block != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) if (edid->blocks == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) hdmi->edid.blocks = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) hdmi->edid.present = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) /* Fall back to a 16:9 aspect ratio */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) hdmi->aspect_ratio.numerator = 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) hdmi->aspect_ratio.denominator = 9;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) /* Disable the EDID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) repeater_write(state, ADV748X_REPEATER_EDID_SZ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) repeater_write(state, ADV748X_REPEATER_EDID_CTL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) if (edid->blocks > 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) edid->blocks = 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) return -E2BIG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) memcpy(hdmi->edid.edid, edid->edid, 128 * edid->blocks);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) hdmi->edid.blocks = edid->blocks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) hdmi->edid.present = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) hdmi->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) edid->edid[0x16]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) err = adv748x_hdmi_edid_write_block(hdmi, 128 * edid->blocks,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) hdmi->edid.edid);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) if (err < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) repeater_write(state, ADV748X_REPEATER_EDID_SZ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) repeater_write(state, ADV748X_REPEATER_EDID_CTL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) ADV748X_REPEATER_EDID_CTL_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) static bool adv748x_hdmi_check_dv_timings(const struct v4l2_dv_timings *timings,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) void *hdl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) const struct adv748x_hdmi_video_standards *stds =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) adv748x_hdmi_video_standards;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) unsigned int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) for (i = 0; stds[i].timings.bt.width; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) if (v4l2_match_dv_timings(timings, &stds[i].timings, 0, false))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) static int adv748x_hdmi_enum_dv_timings(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) struct v4l2_enum_dv_timings *timings)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) return v4l2_enum_dv_timings_cap(timings, &adv748x_hdmi_timings_cap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) adv748x_hdmi_check_dv_timings, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) static int adv748x_hdmi_dv_timings_cap(struct v4l2_subdev *sd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) struct v4l2_dv_timings_cap *cap)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) *cap = adv748x_hdmi_timings_cap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) .enum_mbus_code = adv748x_hdmi_enum_mbus_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) .set_fmt = adv748x_hdmi_set_format,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) .get_fmt = adv748x_hdmi_get_format,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) .get_edid = adv748x_hdmi_get_edid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) .set_edid = adv748x_hdmi_set_edid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) .dv_timings_cap = adv748x_hdmi_dv_timings_cap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) .enum_dv_timings = adv748x_hdmi_enum_dv_timings,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) /* -----------------------------------------------------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) * v4l2_subdev_ops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) static const struct v4l2_subdev_ops adv748x_ops_hdmi = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) .video = &adv748x_video_ops_hdmi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) .pad = &adv748x_pad_ops_hdmi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) /* -----------------------------------------------------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) * Controls
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) static const char * const hdmi_ctrl_patgen_menu[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) "Disabled",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) "Solid Color",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) "Color Bars",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) "Ramp Grey",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) "Ramp Blue",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) "Ramp Red",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) "Checkered"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) static int adv748x_hdmi_s_ctrl(struct v4l2_ctrl *ctrl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) struct adv748x_hdmi *hdmi = adv748x_ctrl_to_hdmi(ctrl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) u8 pattern;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) /* Enable video adjustment first */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) ret = cp_clrset(state, ADV748X_CP_VID_ADJ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) ADV748X_CP_VID_ADJ_ENABLE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) ADV748X_CP_VID_ADJ_ENABLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) switch (ctrl->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) case V4L2_CID_BRIGHTNESS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) ret = cp_write(state, ADV748X_CP_BRI, ctrl->val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) case V4L2_CID_HUE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) ret = cp_write(state, ADV748X_CP_HUE, ctrl->val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) case V4L2_CID_CONTRAST:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) ret = cp_write(state, ADV748X_CP_CON, ctrl->val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) case V4L2_CID_SATURATION:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) ret = cp_write(state, ADV748X_CP_SAT, ctrl->val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) case V4L2_CID_TEST_PATTERN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) pattern = ctrl->val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) /* Pattern is 0-indexed. Ctrl Menu is 1-indexed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) if (pattern) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) pattern--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) pattern |= ADV748X_CP_PAT_GEN_EN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) ret = cp_write(state, ADV748X_CP_PAT_GEN, pattern);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) static const struct v4l2_ctrl_ops adv748x_hdmi_ctrl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) .s_ctrl = adv748x_hdmi_s_ctrl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) static int adv748x_hdmi_init_controls(struct adv748x_hdmi *hdmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) v4l2_ctrl_handler_init(&hdmi->ctrl_hdl, 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) /* Use our mutex for the controls */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) hdmi->ctrl_hdl.lock = &state->mutex;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) V4L2_CID_BRIGHTNESS, ADV748X_CP_BRI_MIN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) ADV748X_CP_BRI_MAX, 1, ADV748X_CP_BRI_DEF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) V4L2_CID_CONTRAST, ADV748X_CP_CON_MIN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) ADV748X_CP_CON_MAX, 1, ADV748X_CP_CON_DEF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) V4L2_CID_SATURATION, ADV748X_CP_SAT_MIN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) ADV748X_CP_SAT_MAX, 1, ADV748X_CP_SAT_DEF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) V4L2_CID_HUE, ADV748X_CP_HUE_MIN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) ADV748X_CP_HUE_MAX, 1, ADV748X_CP_HUE_DEF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) * Todo: V4L2_CID_DV_RX_POWER_PRESENT should also be supported when
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) * interrupts are handled correctly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) v4l2_ctrl_new_std_menu_items(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) V4L2_CID_TEST_PATTERN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) ARRAY_SIZE(hdmi_ctrl_patgen_menu) - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) 0, 0, hdmi_ctrl_patgen_menu);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) hdmi->sd.ctrl_handler = &hdmi->ctrl_hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) if (hdmi->ctrl_hdl.error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) v4l2_ctrl_handler_free(&hdmi->ctrl_hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) return hdmi->ctrl_hdl.error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) return v4l2_ctrl_handler_setup(&hdmi->ctrl_hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) int adv748x_hdmi_init(struct adv748x_hdmi *hdmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) static const struct v4l2_dv_timings cea1280x720 =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) V4L2_DV_BT_CEA_1280X720P30;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) hdmi->timings = cea1280x720;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) /* Initialise a default 16:9 aspect ratio */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) hdmi->aspect_ratio.numerator = 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) hdmi->aspect_ratio.denominator = 9;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) adv748x_subdev_init(&hdmi->sd, state, &adv748x_ops_hdmi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) MEDIA_ENT_F_IO_DTV, "hdmi");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) hdmi->pads[ADV748X_HDMI_SINK].flags = MEDIA_PAD_FL_SINK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) hdmi->pads[ADV748X_HDMI_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) ret = media_entity_pads_init(&hdmi->sd.entity,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) ADV748X_HDMI_NR_PADS, hdmi->pads);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) ret = adv748x_hdmi_init_controls(hdmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) goto err_free_media;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) err_free_media:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) media_entity_cleanup(&hdmi->sd.entity);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) v4l2_device_unregister_subdev(&hdmi->sd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) media_entity_cleanup(&hdmi->sd.entity);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) v4l2_ctrl_handler_free(&hdmi->ctrl_hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) }