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