Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Tim Harvey | 12859 | 99.61% | 2 | 25.00% |
Kuninori Morimoto | 30 | 0.23% | 1 | 12.50% |
Ville Syrjälä | 10 | 0.08% | 1 | 12.50% |
Sakari Ailus | 8 | 0.06% | 2 | 25.00% |
Christophe Jaillet | 1 | 0.01% | 1 | 12.50% |
Hans Verkuil | 1 | 0.01% | 1 | 12.50% |
Total | 12909 | 8 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Gateworks Corporation */ #include <linux/delay.h> #include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/types.h> #include <linux/v4l2-dv-timings.h> #include <linux/videodev2.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-dv-timings.h> #include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/i2c/tda1997x.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <dt-bindings/media/tda1997x.h> #include "tda1997x_regs.h" #define TDA1997X_MBUS_CODES 5 /* debug level */ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); /* Audio formats */ static const char * const audtype_names[] = { "PCM", /* PCM Samples */ "HBR", /* High Bit Rate Audio */ "OBA", /* One-Bit Audio */ "DST" /* Direct Stream Transfer */ }; /* Audio output port formats */ enum audfmt_types { AUDFMT_TYPE_DISABLED = 0, AUDFMT_TYPE_I2S, AUDFMT_TYPE_SPDIF, }; static const char * const audfmt_names[] = { "Disabled", "I2S", "SPDIF", }; /* Video input formats */ static const char * const hdmi_colorspace_names[] = { "RGB", "YUV422", "YUV444", "YUV420", "", "", "", "", }; static const char * const hdmi_colorimetry_names[] = { "", "ITU601", "ITU709", "Extended", }; static const char * const v4l2_quantization_names[] = { "Default", "Full Range (0-255)", "Limited Range (16-235)", }; /* Video output port formats */ static const char * const vidfmt_names[] = { "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */ "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */ "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */ "Invalid", }; /* * Colorspace conversion matrices */ struct color_matrix_coefs { const char *name; /* Input offsets */ s16 offint1; s16 offint2; s16 offint3; /* Coeficients */ s16 p11coef; s16 p12coef; s16 p13coef; s16 p21coef; s16 p22coef; s16 p23coef; s16 p31coef; s16 p32coef; s16 p33coef; /* Output offsets */ s16 offout1; s16 offout2; s16 offout3; }; enum { ITU709_RGBFULL, ITU601_RGBFULL, RGBLIMITED_RGBFULL, RGBLIMITED_ITU601, RGBLIMITED_ITU709, RGBFULL_ITU601, RGBFULL_ITU709, }; /* NB: 4096 is 1.0 using fixed point numbers */ static const struct color_matrix_coefs conv_matrix[] = { { "YUV709 -> RGB full", -256, -2048, -2048, 4769, -2183, -873, 4769, 7343, 0, 4769, 0, 8652, 0, 0, 0, }, { "YUV601 -> RGB full", -256, -2048, -2048, 4769, -3330, -1602, 4769, 6538, 0, 4769, 0, 8264, 256, 256, 256, }, { "RGB limited -> RGB full", -256, -256, -256, 0, 4769, 0, 0, 0, 4769, 4769, 0, 0, 0, 0, 0, }, { "RGB limited -> ITU601", -256, -256, -256, 2404, 1225, 467, -1754, 2095, -341, -1388, -707, 2095, 256, 2048, 2048, }, { "RGB limited -> ITU709", -256, -256, -256, 2918, 867, 295, -1894, 2087, -190, -1607, -477, 2087, 256, 2048, 2048, }, { "RGB full -> ITU601", 0, 0, 0, 2065, 1052, 401, -1506, 1799, -293, -1192, -607, 1799, 256, 2048, 2048, }, { "RGB full -> ITU709", 0, 0, 0, 2506, 745, 253, -1627, 1792, -163, -1380, -410, 1792, 256, 2048, 2048, }, }; static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, V4L2_INIT_BT_TIMINGS( 640, 1920, /* min/max width */ 350, 1200, /* min/max height */ 13000000, 165000000, /* min/max pixelclock */ /* standards */ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, /* capabilities */ V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM ) }; /* regulator supplies */ static const char * const tda1997x_supply_name[] = { "DOVDD", /* Digital I/O supply */ "DVDD", /* Digital Core supply */ "AVDD", /* Analog supply */ }; #define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name) enum tda1997x_type { TDA19971, TDA19973, }; enum tda1997x_hdmi_pads { TDA1997X_PAD_SOURCE, TDA1997X_NUM_PADS, }; struct tda1997x_chip_info { enum tda1997x_type type; const char *name; }; struct tda1997x_state { const struct tda1997x_chip_info *info; struct tda1997x_platform_data pdata; struct i2c_client *client; struct i2c_client *client_cec; struct v4l2_subdev sd; struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES]; struct media_pad pads[TDA1997X_NUM_PADS]; struct mutex lock; struct mutex page_lock; char page; /* detected info from chip */ int chip_revision; char port_30bit; char output_2p5; char tmdsb_clk; char tmdsb_soc; /* status info */ char hdmi_status; char mptrw_in_progress; char activity_status; char input_detect[2]; /* video */ struct hdmi_avi_infoframe avi_infoframe; struct v4l2_hdmi_colorimetry colorimetry; u32 rgb_quantization_range; struct v4l2_dv_timings timings; int fps; const struct color_matrix_coefs *conv; u32 mbus_codes[TDA1997X_MBUS_CODES]; /* available modes */ u32 mbus_code; /* current mode */ u8 vid_fmt; /* controls */ struct v4l2_ctrl_handler hdl; struct v4l2_ctrl *detect_tx_5v_ctrl; struct v4l2_ctrl *rgb_quantization_range_ctrl; /* audio */ u8 audio_ch_alloc; int audio_samplerate; int audio_channels; int audio_samplesize; int audio_type; struct mutex audio_lock; struct snd_pcm_substream *audio_stream; /* EDID */ struct { u8 edid[256]; u32 present; unsigned int blocks; } edid; struct delayed_work delayed_work_enable_hpd; }; static const struct v4l2_event tda1997x_ev_fmt = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; static const struct tda1997x_chip_info tda1997x_chip_info[] = { [TDA19971] = { .type = TDA19971, .name = "tda19971", }, [TDA19973] = { .type = TDA19973, .name = "tda19973", }, }; static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd) { return container_of(sd, struct tda1997x_state, sd); } static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) { return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd; } static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg) { struct tda1997x_state *state = to_state(sd); int val; val = i2c_smbus_read_byte_data(state->client_cec, reg); if (val < 0) { v4l_err(state->client, "read reg error: reg=%2x\n", reg); val = -1; } return val; } static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct tda1997x_state *state = to_state(sd); int ret = 0; ret = i2c_smbus_write_byte_data(state->client_cec, reg, val); if (ret < 0) { v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", reg, val); ret = -1; } return ret; } /* ----------------------------------------------------------------------------- * I2C transfer */ static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page) { struct tda1997x_state *state = to_state(sd); int ret; if (state->page != page) { ret = i2c_smbus_write_byte_data(state->client, REG_CURPAGE_00H, page); if (ret < 0) { v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", REG_CURPAGE_00H, page); return ret; } state->page = page; } return 0; } static inline int io_read(struct v4l2_subdev *sd, u16 reg) { struct tda1997x_state *state = to_state(sd); int val; mutex_lock(&state->page_lock); if (tda1997x_setpage(sd, reg >> 8)) { val = -1; goto out; } val = i2c_smbus_read_byte_data(state->client, reg&0xff); if (val < 0) { v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff); val = -1; goto out; } out: mutex_unlock(&state->page_lock); return val; } static inline long io_read16(struct v4l2_subdev *sd, u16 reg) { int val; long lval = 0; val = io_read(sd, reg); if (val < 0) return val; lval |= (val << 8); val = io_read(sd, reg + 1); if (val < 0) return val; lval |= val; return lval; } static inline long io_read24(struct v4l2_subdev *sd, u16 reg) { int val; long lval = 0; val = io_read(sd, reg); if (val < 0) return val; lval |= (val << 16); val = io_read(sd, reg + 1); if (val < 0) return val; lval |= (val << 8); val = io_read(sd, reg + 2); if (val < 0) return val; lval |= val; return lval; } static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data) { int i; int sz = 0; int val; for (i = 0; i < len; i++) { val = io_read(sd, reg + i); if (val < 0) break; data[i] = val; sz++; } return sz; } static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val) { struct tda1997x_state *state = to_state(sd); s32 ret = 0; mutex_lock(&state->page_lock); if (tda1997x_setpage(sd, reg >> 8)) { ret = -1; goto out; } ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val); if (ret < 0) { v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", reg&0xff, val); ret = -1; goto out; } out: mutex_unlock(&state->page_lock); return ret; } static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val) { int ret; ret = io_write(sd, reg, (val >> 8) & 0xff); if (ret < 0) return ret; ret = io_write(sd, reg + 1, val & 0xff); if (ret < 0) return ret; return 0; } static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val) { int ret; ret = io_write(sd, reg, (val >> 16) & 0xff); if (ret < 0) return ret; ret = io_write(sd, reg + 1, (val >> 8) & 0xff); if (ret < 0) return ret; ret = io_write(sd, reg + 2, val & 0xff); if (ret < 0) return ret; return 0; } /* ----------------------------------------------------------------------------- * Hotplug */ enum hpd_mode { HPD_LOW_BP, /* HPD low and pulse of at least 100ms */ HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */ HPD_HIGH_BP, /* HIGH */ HPD_HIGH_OTHER, HPD_PULSE, /* HPD low pulse */ }; /* manual HPD (Hot Plug Detect) control */ static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode) { u8 hpd_auto, hpd_pwr, hpd_man; hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL); hpd_pwr = io_read(sd, REG_HPD_POWER); hpd_man = io_read(sd, REG_HPD_MAN_CTRL); /* mask out unused bits */ hpd_man &= (HPD_MAN_CTRL_HPD_PULSE | HPD_MAN_CTRL_5VEN | HPD_MAN_CTRL_HPD_B | HPD_MAN_CTRL_HPD_A); switch (mode) { /* HPD low and pulse of at least 100ms */ case HPD_LOW_BP: /* hpd_bp=0 */ hpd_pwr &= ~HPD_POWER_BP_MASK; /* disable HPD_A and HPD_B */ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); io_write(sd, REG_HPD_POWER, hpd_pwr); io_write(sd, REG_HPD_MAN_CTRL, hpd_man); break; /* HPD high */ case HPD_HIGH_BP: /* hpd_bp=1 */ hpd_pwr &= ~HPD_POWER_BP_MASK; hpd_pwr |= 1 << HPD_POWER_BP_SHIFT; io_write(sd, REG_HPD_POWER, hpd_pwr); break; /* HPD low and pulse of at least 100ms */ case HPD_LOW_OTHER: /* disable HPD_A and HPD_B */ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); /* hp_other=0 */ hpd_auto &= ~HPD_AUTO_HP_OTHER; io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); io_write(sd, REG_HPD_MAN_CTRL, hpd_man); break; /* HPD high */ case HPD_HIGH_OTHER: hpd_auto |= HPD_AUTO_HP_OTHER; io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); break; /* HPD low pulse */ case HPD_PULSE: /* disable HPD_A and HPD_B */ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); io_write(sd, REG_HPD_MAN_CTRL, hpd_man); break; } return 0; } static void tda1997x_delayed_work_enable_hpd(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct tda1997x_state *state = container_of(dwork, struct tda1997x_state, delayed_work_enable_hpd); struct v4l2_subdev *sd = &state->sd; v4l2_dbg(2, debug, sd, "%s:\n", __func__); /* Set HPD high */ tda1997x_manual_hpd(sd, HPD_HIGH_OTHER); tda1997x_manual_hpd(sd, HPD_HIGH_BP); state->edid.present = 1; } static void tda1997x_disable_edid(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); v4l2_dbg(1, debug, sd, "%s\n", __func__); cancel_delayed_work_sync(&state->delayed_work_enable_hpd); /* Set HPD low */ tda1997x_manual_hpd(sd, HPD_LOW_BP); } static void tda1997x_enable_edid(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); v4l2_dbg(1, debug, sd, "%s\n", __func__); /* Enable hotplug after 100ms */ schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10); } /* ----------------------------------------------------------------------------- * Signal Control */ /* * configure vid_fmt based on mbus_code */ static int tda1997x_setup_format(struct tda1997x_state *state, u32 code) { v4l_dbg(1, debug, state->client, "%s code=0x%x\n", __func__, code); switch (code) { case MEDIA_BUS_FMT_RGB121212_1X36: case MEDIA_BUS_FMT_RGB888_1X24: case MEDIA_BUS_FMT_YUV12_1X36: case MEDIA_BUS_FMT_YUV8_1X24: state->vid_fmt = OF_FMT_444; break; case MEDIA_BUS_FMT_UYVY12_1X24: case MEDIA_BUS_FMT_UYVY10_1X20: case MEDIA_BUS_FMT_UYVY8_1X16: state->vid_fmt = OF_FMT_422_SMPT; break; case MEDIA_BUS_FMT_UYVY12_2X12: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_UYVY8_2X8: state->vid_fmt = OF_FMT_422_CCIR; break; default: v4l_err(state->client, "incompatible format (0x%x)\n", code); return -EINVAL; } v4l_dbg(1, debug, state->client, "%s code=0x%x fmt=%s\n", __func__, code, vidfmt_names[state->vid_fmt]); state->mbus_code = code; return 0; } /* * The color conversion matrix will convert between the colorimetry of the * HDMI input to the desired output format RGB|YUV. RGB output is to be * full-range and YUV is to be limited range. * * RGB full-range uses values from 0 to 255 which is recommended on a monitor * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is * typically recommended on a TV. */ static void tda1997x_configure_csc(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); struct hdmi_avi_infoframe *avi = &state->avi_infoframe; struct v4l2_hdmi_colorimetry *c = &state->colorimetry; /* Blanking code values depend on output colorspace (RGB or YUV) */ struct blanking_codes { s16 code_gy; s16 code_bu; s16 code_rv; }; static const struct blanking_codes rgb_blanking = { 64, 64, 64 }; static const struct blanking_codes yuv_blanking = { 64, 512, 512 }; const struct blanking_codes *blanking_codes = NULL; u8 reg; v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n", hdmi_colorspace_names[avi->colorspace], v4l2_quantization_names[c->quantization], vidfmt_names[state->vid_fmt]); state->conv = NULL; switch (state->vid_fmt) { /* RGB output */ case OF_FMT_444: blanking_codes = &rgb_blanking; if (c->colorspace == V4L2_COLORSPACE_SRGB) { if (c->quantization == V4L2_QUANTIZATION_LIM_RANGE) state->conv = &conv_matrix[RGBLIMITED_RGBFULL]; } else { if (c->colorspace == V4L2_COLORSPACE_REC709) state->conv = &conv_matrix[ITU709_RGBFULL]; else if (c->colorspace == V4L2_COLORSPACE_SMPTE170M) state->conv = &conv_matrix[ITU601_RGBFULL]; } break; /* YUV output */ case OF_FMT_422_SMPT: /* semi-planar */ case OF_FMT_422_CCIR: /* CCIR656 */ blanking_codes = &yuv_blanking; if ((c->colorspace == V4L2_COLORSPACE_SRGB) && (c->quantization == V4L2_QUANTIZATION_FULL_RANGE)) { if (state->timings.bt.height <= 576) state->conv = &conv_matrix[RGBFULL_ITU601]; else state->conv = &conv_matrix[RGBFULL_ITU709]; } else if ((c->colorspace == V4L2_COLORSPACE_SRGB) && (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)) { if (state->timings.bt.height <= 576) state->conv = &conv_matrix[RGBLIMITED_ITU601]; else state->conv = &conv_matrix[RGBLIMITED_ITU709]; } break; } if (state->conv) { v4l_dbg(1, debug, state->client, "%s\n", state->conv->name); /* enable matrix conversion */ reg = io_read(sd, REG_VDP_CTRL); reg &= ~VDP_CTRL_MATRIX_BP; io_write(sd, REG_VDP_CTRL, reg); /* offset inputs */ io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1); io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2); io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3); /* coefficients */ io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef); io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef); io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef); io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef); io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef); io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef); io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef); io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef); io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef); /* offset outputs */ io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1); io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2); io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3); } else { /* disable matrix conversion */ reg = io_read(sd, REG_VDP_CTRL); reg |= VDP_CTRL_MATRIX_BP; io_write(sd, REG_VDP_CTRL, reg); } /* SetBlankingCodes */ if (blanking_codes) { io_write16(sd, REG_BLK_GY, blanking_codes->code_gy); io_write16(sd, REG_BLK_BU, blanking_codes->code_bu); io_write16(sd, REG_BLK_RV, blanking_codes->code_rv); } } /* Configure frame detection window and VHREF timing generator */ static void tda1997x_configure_vhref(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); const struct v4l2_bt_timings *bt = &state->timings.bt; int width, lines; u16 href_start, href_end; u16 vref_f1_start, vref_f2_start; u8 vref_f1_width, vref_f2_width; u8 field_polarity; u16 fieldref_f1_start, fieldref_f2_start; u8 reg; href_start = bt->hbackporch + bt->hsync + 1; href_end = href_start + bt->width; vref_f1_start = bt->height + bt->vbackporch + bt->vsync + bt->il_vbackporch + bt->il_vsync + bt->il_vfrontporch; vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch; vref_f2_start = 0; vref_f2_width = 0; fieldref_f1_start = 0; fieldref_f2_start = 0; if (bt->interlaced) { vref_f2_start = (bt->height / 2) + (bt->il_vbackporch + bt->il_vsync - 1); vref_f2_width = bt->il_vbackporch + bt->il_vsync + bt->il_vfrontporch; fieldref_f2_start = vref_f2_start + bt->il_vfrontporch + fieldref_f1_start; } field_polarity = 0; width = V4L2_DV_BT_FRAME_WIDTH(bt); lines = V4L2_DV_BT_FRAME_HEIGHT(bt); /* * Configure Frame Detection Window: * horiz area where the VHREF module consider a VSYNC a new frame */ io_write16(sd, REG_FDW_S, 0x2ef); /* start position */ io_write16(sd, REG_FDW_E, 0x141); /* end position */ /* Set Pixel And Line Counters */ if (state->chip_revision == 0) io_write16(sd, REG_PXCNT_PR, 4); else io_write16(sd, REG_PXCNT_PR, 1); io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF); io_write16(sd, REG_LCNT_PR, 1); io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF); /* * Configure the VHRef timing generator responsible for rebuilding all * horiz and vert synch and ref signals from its input allowing auto * detection algorithms and forcing predefined modes (480i & 576i) */ reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT; io_write(sd, REG_VHREF_CTRL, reg); /* * Configure the VHRef timing values. In case the VHREF generator has * been configured in manual mode, this will allow to manually set all * horiz and vert ref values (non-active pixel areas) of the generator * and allows setting the frame reference params. */ /* horizontal reference start/end */ io_write16(sd, REG_HREF_S, href_start & MASK_VHREF); io_write16(sd, REG_HREF_E, href_end & MASK_VHREF); /* vertical reference f1 start/end */ io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF); io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width); /* vertical reference f2 start/end */ io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF); io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width); /* F1/F2 FREF, field polarity */ reg = fieldref_f1_start & MASK_VHREF; reg |= field_polarity << 8; io_write16(sd, REG_FREF_F1_S, reg); reg = fieldref_f2_start & MASK_VHREF; io_write16(sd, REG_FREF_F2_S, reg); } /* Configure Video Output port signals */ static int tda1997x_configure_vidout(struct tda1997x_state *state) { struct v4l2_subdev *sd = &state->sd; struct tda1997x_platform_data *pdata = &state->pdata; u8 prefilter; u8 reg; /* Configure pixel clock generator: delay, polarity, rate */ reg = (state->vid_fmt == OF_FMT_422_CCIR) ? PCLK_SEL_X2 : PCLK_SEL_X1; reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT; reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT; io_write(sd, REG_PCLK, reg); /* Configure pre-filter */ prefilter = 0; /* filters off */ /* YUV422 mode requires conversion */ if ((state->vid_fmt == OF_FMT_422_SMPT) || (state->vid_fmt == OF_FMT_422_CCIR)) { /* 2/7 taps for Rv and Bu */ prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT | FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT; } io_write(sd, REG_FILTERS_CTRL, prefilter); /* Configure video port */ reg = state->vid_fmt & OF_FMT_MASK; if (state->vid_fmt == OF_FMT_422_CCIR) reg |= (OF_BLK | OF_TRC); reg |= OF_VP_ENABLE; io_write(sd, REG_OF, reg); /* Configure formatter and conversions */ reg = io_read(sd, REG_VDP_CTRL); /* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */ if (!prefilter) reg |= VDP_CTRL_PREFILTER_BP; else reg &= ~VDP_CTRL_PREFILTER_BP; /* formatter is needed for YUV422 and for trc/blc codes */ if (state->vid_fmt == OF_FMT_444) reg |= VDP_CTRL_FORMATTER_BP; /* formatter and compdel needed for timing/blanking codes */ else reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP); /* activate compdel for small sync delays */ if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) reg &= ~VDP_CTRL_COMPDEL_BP; io_write(sd, REG_VDP_CTRL, reg); /* Configure DE output signal: delay, polarity, and source */ reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT | pdata->vidout_inv_de << DE_FREF_INV_SHIFT | pdata->vidout_sel_de << DE_FREF_SEL_SHIFT; io_write(sd, REG_DE_FREF, reg); /* Configure HS/HREF output signal: delay, polarity, and source */ if (state->vid_fmt != OF_FMT_422_CCIR) { reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT | pdata->vidout_inv_hs << HS_HREF_INV_SHIFT | pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT; } else reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT; io_write(sd, REG_HS_HREF, reg); /* Configure VS/VREF output signal: delay, polarity, and source */ if (state->vid_fmt != OF_FMT_422_CCIR) { reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT | pdata->vidout_inv_vs << VS_VREF_INV_SHIFT | pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT; } else reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT; io_write(sd, REG_VS_VREF, reg); return 0; } /* Configure Audio output port signals */ static int tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) { struct tda1997x_state *state = to_state(sd); struct tda1997x_platform_data *pdata = &state->pdata; bool sp_used_by_fifo = 1; u8 reg; if (!pdata->audout_format) return 0; /* channel assignment (CEA-861-D Table 20) */ io_write(sd, REG_AUDIO_PATH, channel_assignment); /* Audio output configuration */ reg = 0; switch (pdata->audout_format) { case AUDFMT_TYPE_I2S: reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT; break; case AUDFMT_TYPE_SPDIF: reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT; break; } switch (state->audio_type) { case AUDCFG_TYPE_PCM: reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT; break; case AUDCFG_TYPE_OBA: reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT; break; case AUDCFG_TYPE_DST: reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT; sp_used_by_fifo = 0; break; case AUDCFG_TYPE_HBR: reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT; if (pdata->audout_layout == 1) { /* demuxed via AP0:AP3 */ reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT; if (pdata->audout_format == AUDFMT_TYPE_SPDIF) sp_used_by_fifo = 0; } else { /* straight via AP0 */ reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT; } break; } if (pdata->audout_width == 32) reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT; else reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT; /* automatic hardware mute */ if (pdata->audio_auto_mute) reg |= AUDCFG_AUTO_MUTE_EN; /* clock polarity */ if (pdata->audout_invert_clk) reg |= AUDCFG_CLK_INVERT; io_write(sd, REG_AUDCFG, reg); /* audio layout */ reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0; if (!pdata->audout_layoutauto) reg |= AUDIO_LAYOUT_MANUAL; if (sp_used_by_fifo) reg |= AUDIO_LAYOUT_SP_FLAG; io_write(sd, REG_AUDIO_LAYOUT, reg); /* FIFO Latency value */ io_write(sd, REG_FIFO_LATENCY_VAL, 0x80); /* Audio output port config */ if (sp_used_by_fifo) { reg = AUDIO_OUT_ENABLE_AP0; if (channel_assignment >= 0x01) reg |= AUDIO_OUT_ENABLE_AP1; if (channel_assignment >= 0x04) reg |= AUDIO_OUT_ENABLE_AP2; if (channel_assignment >= 0x0c) reg |= AUDIO_OUT_ENABLE_AP3; /* specific cases where AP1 is not used */ if ((channel_assignment == 0x04) || (channel_assignment == 0x08) || (channel_assignment == 0x0c) || (channel_assignment == 0x10) || (channel_assignment == 0x14) || (channel_assignment == 0x18) || (channel_assignment == 0x1c)) reg &= ~AUDIO_OUT_ENABLE_AP1; /* specific cases where AP2 is not used */ if ((channel_assignment >= 0x14) && (channel_assignment <= 0x17)) reg &= ~AUDIO_OUT_ENABLE_AP2; } else { reg = AUDIO_OUT_ENABLE_AP3 | AUDIO_OUT_ENABLE_AP2 | AUDIO_OUT_ENABLE_AP1 | AUDIO_OUT_ENABLE_AP0; } if (pdata->audout_format == AUDFMT_TYPE_I2S) reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS); io_write(sd, REG_AUDIO_OUT_ENABLE, reg); /* reset test mode to normal audio freq auto selection */ io_write(sd, REG_TEST_MODE, 0x00); return 0; } /* Soft Reset of specific hdmi info */ static int tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus) { u8 reg; /* reset infoframe engine packets */ reg = io_read(sd, REG_HDMI_INFO_RST); io_write(sd, REG_HDMI_INFO_RST, info_rst); /* if infoframe engine has been reset clear INT_FLG_MODE */ if (reg & RESET_IF) { reg = io_read(sd, REG_INT_FLG_CLR_MODE); io_write(sd, REG_INT_FLG_CLR_MODE, reg); } /* Disable REFTIM to restart start-up-sequencer (SUS) */ reg = io_read(sd, REG_RATE_CTRL); reg &= ~RATE_REFTIM_ENABLE; if (!reset_sus) reg |= RATE_REFTIM_ENABLE; reg = io_write(sd, REG_RATE_CTRL, reg); return 0; } static void tda1997x_power_mode(struct tda1997x_state *state, bool enable) { struct v4l2_subdev *sd = &state->sd; u8 reg; if (enable) { /* Automatic control of TMDS */ io_write(sd, REG_PON_OVR_EN, PON_DIS); /* Enable current bias unit */ io_write(sd, REG_CFG1, PON_EN); /* Enable deep color PLL */ io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS); /* Output buffers active */ reg = io_read(sd, REG_OF); reg &= ~OF_VP_ENABLE; io_write(sd, REG_OF, reg); } else { /* Power down EDID mode sequence */ /* Output buffers in HiZ */ reg = io_read(sd, REG_OF); reg |= OF_VP_ENABLE; io_write(sd, REG_OF, reg); /* Disable deep color PLL */ io_write(sd, REG_DEEP_PLL7_BYP, PON_EN); /* Disable current bias unit */ io_write(sd, REG_CFG1, PON_DIS); /* Manual control of TMDS */ io_write(sd, REG_PON_OVR_EN, PON_EN); } } static bool tda1997x_detect_tx_5v(struct v4l2_subdev *sd) { u8 reg = io_read(sd, REG_DETECT_5V); return ((reg & DETECT_5V_SEL) ? 1 : 0); } static bool tda1997x_detect_tx_hpd(struct v4l2_subdev *sd) { u8 reg = io_read(sd, REG_DETECT_5V); return ((reg & DETECT_HPD) ? 1 : 0); } static int tda1997x_detect_std(struct tda1997x_state *state, struct v4l2_dv_timings *timings) { struct v4l2_subdev *sd = &state->sd; u32 vper; u16 hper; u16 hsper; int i; /* * Read the FMT registers * REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles * REG_H_PER: Period of a line in MCLK(27MHz) cycles * REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles */ vper = io_read24(sd, REG_V_PER) & MASK_VPER; hper = io_read16(sd, REG_H_PER) & MASK_HPER; hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper); if (!vper || !hper || !hsper) return -ENOLINK; for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { const struct v4l2_bt_timings *bt; u32 lines, width, _hper, _hsper; u32 vmin, vmax, hmin, hmax, hsmin, hsmax; bool vmatch, hmatch, hsmatch; bt = &v4l2_dv_timings_presets[i].bt; width = V4L2_DV_BT_FRAME_WIDTH(bt); lines = V4L2_DV_BT_FRAME_HEIGHT(bt); _hper = (u32)bt->pixelclock / width; if (bt->interlaced) lines /= 2; /* vper +/- 0.7% */ vmin = ((27000000 / 1000) * 993) / _hper * lines; vmax = ((27000000 / 1000) * 1007) / _hper * lines; /* hper +/- 1.0% */ hmin = ((27000000 / 100) * 99) / _hper; hmax = ((27000000 / 100) * 101) / _hper; /* hsper +/- 2 (take care to avoid 32bit overflow) */ _hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000); hsmin = _hsper - 2; hsmax = _hsper + 2; /* vmatch matches the framerate */ vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0; /* hmatch matches the width */ hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0; /* hsmatch matches the hswidth */ hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0; if (hmatch && vmatch && hsmatch) { v4l2_print_dv_timings(sd->name, "Detected format: ", &v4l2_dv_timings_presets[i], false); if (timings) *timings = v4l2_dv_timings_presets[i]; return 0; } } v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n", vper, hper, hsper); return -ERANGE; } /* some sort of errata workaround for chip revision 0 (N1) */ static void tda1997x_reset_n1(struct tda1997x_state *state) { struct v4l2_subdev *sd = &state->sd; u8 reg; /* clear HDMI mode flag in BCAPS */ io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK); io_write(sd, REG_PON_OVR_EN, PON_EN); io_write(sd, REG_PON_CBIAS, PON_EN); io_write(sd, REG_PON_PLL, PON_EN); reg = io_read(sd, REG_MODE_REC_CFG1); reg &= ~0x06; reg |= 0x02; io_write(sd, REG_MODE_REC_CFG1, reg); io_write(sd, REG_CLK_CFG, CLK_CFG_DIS); io_write(sd, REG_PON_OVR_EN, PON_DIS); reg = io_read(sd, REG_MODE_REC_CFG1); reg &= ~0x06; io_write(sd, REG_MODE_REC_CFG1, reg); } /* * Activity detection must only be notified when stable_clk_x AND active_x * bits are set to 1. If only stable_clk_x bit is set to 1 but not * active_x, it means that the TMDS clock is not in the defined range * and activity detection must not be notified. */ static u8 tda1997x_read_activity_status_regs(struct v4l2_subdev *sd) { u8 reg, status = 0; /* Read CLK_A_STATUS register */ reg = io_read(sd, REG_CLK_A_STATUS); /* ignore if not active */ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) reg &= ~MASK_CLK_STABLE; status |= ((reg & MASK_CLK_STABLE) >> 2); /* Read CLK_B_STATUS register */ reg = io_read(sd, REG_CLK_B_STATUS); /* ignore if not active */ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) reg &= ~MASK_CLK_STABLE; status |= ((reg & MASK_CLK_STABLE) >> 1); /* Read the SUS_STATUS register */ reg = io_read(sd, REG_SUS_STATUS); /* If state = 5 => TMDS is locked */ if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) status |= MASK_SUS_STATE; else status &= ~MASK_SUS_STATE; return status; } static void set_rgb_quantization_range(struct tda1997x_state *state) { struct v4l2_hdmi_colorimetry *c = &state->colorimetry; state->colorimetry = v4l2_hdmi_rx_colorimetry(&state->avi_infoframe, NULL, state->timings.bt.height); /* If ycbcr_enc is V4L2_YCBCR_ENC_DEFAULT, we receive RGB */ if (c->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) { switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_LIMITED: c->quantization = V4L2_QUANTIZATION_FULL_RANGE; break; case V4L2_DV_RGB_RANGE_FULL: c->quantization = V4L2_QUANTIZATION_LIM_RANGE; break; } } v4l_dbg(1, debug, state->client, "colorspace=%d/%d colorimetry=%d range=%s content=%d\n", state->avi_infoframe.colorspace, c->colorspace, state->avi_infoframe.colorimetry, v4l2_quantization_names[c->quantization], state->avi_infoframe.content_type); } /* parse an infoframe and do some sanity checks on it */ static unsigned int tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) { struct v4l2_subdev *sd = &state->sd; union hdmi_infoframe frame; u8 buffer[40]; u8 reg; int len, err; /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", len, addr, buffer[0]); return err; } hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); switch (frame.any.type) { /* Audio InfoFrame: see HDMI spec 8.2.2 */ case HDMI_INFOFRAME_TYPE_AUDIO: /* sample rate */ switch (frame.audio.sample_frequency) { case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: state->audio_samplerate = 32000; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: state->audio_samplerate = 44100; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: state->audio_samplerate = 48000; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: state->audio_samplerate = 88200; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: state->audio_samplerate = 96000; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: state->audio_samplerate = 176400; break; case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: state->audio_samplerate = 192000; break; default: case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: break; } /* sample size */ switch (frame.audio.sample_size) { case HDMI_AUDIO_SAMPLE_SIZE_16: state->audio_samplesize = 16; break; case HDMI_AUDIO_SAMPLE_SIZE_20: state->audio_samplesize = 20; break; case HDMI_AUDIO_SAMPLE_SIZE_24: state->audio_samplesize = 24; break; case HDMI_AUDIO_SAMPLE_SIZE_STREAM: default: break; } /* Channel Count */ state->audio_channels = frame.audio.channels; if (frame.audio.channel_allocation && frame.audio.channel_allocation != state->audio_ch_alloc) { /* use the channel assignment from the infoframe */ state->audio_ch_alloc = frame.audio.channel_allocation; tda1997x_configure_audout(sd, state->audio_ch_alloc); /* reset the audio FIFO */ tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); } break; /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ case HDMI_INFOFRAME_TYPE_AVI: state->avi_infoframe = frame.avi; set_rgb_quantization_range(state); /* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */ reg = io_read(sd, REG_PIX_REPEAT); reg &= ~PIX_REPEAT_MASK_UP_SEL; if (frame.avi.colorspace == HDMI_COLORSPACE_YUV422) reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT); io_write(sd, REG_PIX_REPEAT, reg); /* ConfigurePixelRepeater: repeat n-times each pixel */ reg = io_read(sd, REG_PIX_REPEAT); reg &= ~PIX_REPEAT_MASK_REP; reg |= frame.avi.pixel_repeat; io_write(sd, REG_PIX_REPEAT, reg); /* configure the receiver with the new colorspace */ tda1997x_configure_csc(sd); break; default: break; } return 0; } static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 reg, source; source = io_read(sd, REG_INT_FLG_CLR_SUS); io_write(sd, REG_INT_FLG_CLR_SUS, source); if (source & MASK_MPT) { /* reset MTP in use flag if set */ if (state->mptrw_in_progress) state->mptrw_in_progress = 0; } if (source & MASK_SUS_END) { /* reset audio FIFO */ reg = io_read(sd, REG_HDMI_INFO_RST); reg |= MASK_SR_FIFO_FIFO_CTRL; io_write(sd, REG_HDMI_INFO_RST, reg); reg &= ~MASK_SR_FIFO_FIFO_CTRL; io_write(sd, REG_HDMI_INFO_RST, reg); /* reset HDMI flags */ state->hdmi_status = 0; } /* filter FMT interrupt based on SUS state */ reg = io_read(sd, REG_SUS_STATUS); if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) || (source & MASK_MPT)) { source &= ~MASK_FMT; } if (source & (MASK_FMT | MASK_SUS_END)) { reg = io_read(sd, REG_SUS_STATUS); if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) { v4l_err(state->client, "BAD SUS STATUS\n"); return; } if (debug) tda1997x_detect_std(state, NULL); /* notify user of change in resolution */ v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt); } } static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 source; source = io_read(sd, REG_INT_FLG_CLR_DDC); io_write(sd, REG_INT_FLG_CLR_DDC, source); if (source & MASK_EDID_MTP) { /* reset MTP in use flag if set */ if (state->mptrw_in_progress) state->mptrw_in_progress = 0; } /* Detection of +5V */ if (source & MASK_DET_5V) { v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, tda1997x_detect_tx_5v(sd)); } } static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 reg, source; u8 irq_status; source = io_read(sd, REG_INT_FLG_CLR_RATE); io_write(sd, REG_INT_FLG_CLR_RATE, source); /* read status regs */ irq_status = tda1997x_read_activity_status_regs(sd); /* * read clock status reg until INT_FLG_CLR_RATE is still 0 * after the read to make sure its the last one */ reg = source; while (reg != 0) { irq_status = tda1997x_read_activity_status_regs(sd); reg = io_read(sd, REG_INT_FLG_CLR_RATE); io_write(sd, REG_INT_FLG_CLR_RATE, reg); source |= reg; } /* we only pay attention to stability change events */ if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { int input = (source & MASK_RATE_A_ST)?0:1; u8 mask = 1<<input; /* state change */ if ((irq_status & mask) != (state->activity_status & mask)) { /* activity lost */ if ((irq_status & mask) == 0) { v4l_info(state->client, "HDMI-%c: Digital Activity Lost\n", input+'A'); /* bypass up/down sampler and pixel repeater */ reg = io_read(sd, REG_PIX_REPEAT); reg &= ~PIX_REPEAT_MASK_UP_SEL; reg &= ~PIX_REPEAT_MASK_REP; io_write(sd, REG_PIX_REPEAT, reg); if (state->chip_revision == 0) tda1997x_reset_n1(state); state->input_detect[input] = 0; v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt); } /* activity detected */ else { v4l_info(state->client, "HDMI-%c: Digital Activity Detected\n", input+'A'); state->input_detect[input] = 1; } /* hold onto current state */ state->activity_status = (irq_status & mask); } } } static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 source; source = io_read(sd, REG_INT_FLG_CLR_INFO); io_write(sd, REG_INT_FLG_CLR_INFO, source); /* Audio infoframe */ if (source & MASK_AUD_IF) { tda1997x_parse_infoframe(state, AUD_IF); source &= ~MASK_AUD_IF; } /* Source Product Descriptor infoframe change */ if (source & MASK_SPD_IF) { tda1997x_parse_infoframe(state, SPD_IF); source &= ~MASK_SPD_IF; } /* Auxiliary Video Information infoframe */ if (source & MASK_AVI_IF) { tda1997x_parse_infoframe(state, AVI_IF); source &= ~MASK_AVI_IF; } } static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 reg, source; source = io_read(sd, REG_INT_FLG_CLR_AUDIO); io_write(sd, REG_INT_FLG_CLR_AUDIO, source); /* reset audio FIFO on FIFO pointer error or audio mute */ if (source & MASK_ERROR_FIFO_PT || source & MASK_MUTE_FLG) { /* audio reset audio FIFO */ reg = io_read(sd, REG_SUS_STATUS); if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) { reg = io_read(sd, REG_HDMI_INFO_RST); reg |= MASK_SR_FIFO_FIFO_CTRL; io_write(sd, REG_HDMI_INFO_RST, reg); reg &= ~MASK_SR_FIFO_FIFO_CTRL; io_write(sd, REG_HDMI_INFO_RST, reg); /* reset channel status IT if present */ source &= ~(MASK_CH_STATE); } } if (source & MASK_AUDIO_FREQ_FLG) { static const int freq[] = { 0, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; reg = io_read(sd, REG_AUDIO_FREQ); state->audio_samplerate = freq[reg & 7]; v4l_info(state->client, "Audio Frequency Change: %dHz\n", state->audio_samplerate); } if (source & MASK_AUDIO_FLG) { reg = io_read(sd, REG_AUDIO_FLAGS); if (reg & BIT(AUDCFG_TYPE_DST)) state->audio_type = AUDCFG_TYPE_DST; if (reg & BIT(AUDCFG_TYPE_OBA)) state->audio_type = AUDCFG_TYPE_OBA; if (reg & BIT(AUDCFG_TYPE_HBR)) state->audio_type = AUDCFG_TYPE_HBR; if (reg & BIT(AUDCFG_TYPE_PCM)) state->audio_type = AUDCFG_TYPE_PCM; v4l_info(state->client, "Audio Type: %s\n", audtype_names[state->audio_type]); } } static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags) { struct v4l2_subdev *sd = &state->sd; u8 reg, source; source = io_read(sd, REG_INT_FLG_CLR_HDCP); io_write(sd, REG_INT_FLG_CLR_HDCP, source); /* reset MTP in use flag if set */ if (source & MASK_HDCP_MTP) state->mptrw_in_progress = 0; if (source & MASK_STATE_C5) { /* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */ reg = io_read(sd, REG_INT_MASK_TOP); reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); io_write(sd, REG_INT_MASK_TOP, reg); *flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); } } static irqreturn_t tda1997x_isr_thread(int irq, void *d) { struct tda1997x_state *state = d; struct v4l2_subdev *sd = &state->sd; u8 flags; mutex_lock(&state->lock); do { /* read interrupt flags */ flags = io_read(sd, REG_INT_FLG_CLR_TOP); if (flags == 0) break; /* SUS interrupt source (Input activity events) */ if (flags & INTERRUPT_SUS) tda1997x_irq_sus(state, &flags); /* DDC interrupt source (Display Data Channel) */ else if (flags & INTERRUPT_DDC) tda1997x_irq_ddc(state, &flags); /* RATE interrupt source (Digital Input activity) */ else if (flags & INTERRUPT_RATE) tda1997x_irq_rate(state, &flags); /* Infoframe change interrupt */ else if (flags & INTERRUPT_INFO) tda1997x_irq_info(state, &flags); /* Audio interrupt source: * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err */ else if (flags & INTERRUPT_AUDIO) tda1997x_irq_audio(state, &flags); /* HDCP interrupt source (content protection) */ if (flags & INTERRUPT_HDCP) tda1997x_irq_hdcp(state, &flags); } while (flags != 0); mutex_unlock(&state->lock); return IRQ_HANDLED; } /* ----------------------------------------------------------------------------- * v4l2_subdev_video_ops */ static int tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct tda1997x_state *state = to_state(sd); u32 vper; u16 hper; u16 hsper; mutex_lock(&state->lock); vper = io_read24(sd, REG_V_PER) & MASK_VPER; hper = io_read16(sd, REG_H_PER) & MASK_HPER; hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; /* * The tda1997x supports A/B inputs but only a single output. * The irq handler monitors for timing changes on both inputs and * sets the input_detect array to 0|1 depending on signal presence. * I believe selection of A vs B is automatic. * * The vper/hper/hsper registers provide the frame period, line period * and horiz sync period (units of MCLK clock cycles (27MHz)) and * testing shows these values to be random if no signal is present * or locked. */ v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n", state->input_detect[0], state->input_detect[1], vper, hper, hsper); if (!state->input_detect[0] && !state->input_detect[1]) *status = V4L2_IN_ST_NO_SIGNAL; else if (!vper || !hper || !hsper) *status = V4L2_IN_ST_NO_SYNC; else *status = 0; mutex_unlock(&state->lock); return 0; }; static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s\n", __func__); if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) return 0; /* no changes */ if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap, NULL, NULL)) return -ERANGE; mutex_lock(&state->lock); state->timings = *timings; /* setup frame detection window and VHREF timing generator */ tda1997x_configure_vhref(sd); /* configure colorspace conversion */ tda1997x_configure_csc(sd); mutex_unlock(&state->lock); return 0; } static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s\n", __func__); mutex_lock(&state->lock); *timings = state->timings; mutex_unlock(&state->lock); return 0; } static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s\n", __func__); memset(timings, 0, sizeof(struct v4l2_dv_timings)); mutex_lock(&state->lock); tda1997x_detect_std(state, timings); mutex_unlock(&state->lock); return 0; } static const struct v4l2_subdev_video_ops tda1997x_video_ops = { .g_input_status = tda1997x_g_input_status, .s_dv_timings = tda1997x_s_dv_timings, .g_dv_timings = tda1997x_g_dv_timings, .query_dv_timings = tda1997x_query_dv_timings, }; /* ----------------------------------------------------------------------------- * v4l2_subdev_pad_ops */ static int tda1997x_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; mf = v4l2_subdev_get_try_format(sd, cfg, 0); mf->code = state->mbus_codes[0]; return 0; } static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s %d\n", __func__, code->index); if (code->index >= ARRAY_SIZE(state->mbus_codes)) return -EINVAL; if (!state->mbus_codes[code->index]) return -EINVAL; code->code = state->mbus_codes[code->index]; return 0; } static void tda1997x_fill_format(struct tda1997x_state *state, struct v4l2_mbus_framefmt *format) { const struct v4l2_bt_timings *bt; memset(format, 0, sizeof(*format)); bt = &state->timings.bt; format->width = bt->width; format->height = bt->height; format->colorspace = state->colorimetry.colorspace; format->field = (bt->interlaced) ? V4L2_FIELD_SEQ_TB : V4L2_FIELD_NONE; } static int tda1997x_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s pad=%d which=%d\n", __func__, format->pad, format->which); tda1997x_fill_format(state, &format->format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); format->format.code = fmt->code; } else format->format.code = state->mbus_code; return 0; } static int tda1997x_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct tda1997x_state *state = to_state(sd); u32 code = 0; int i; v4l_dbg(1, debug, state->client, "%s pad=%d which=%d fmt=0x%x\n", __func__, format->pad, format->which, format->format.code); for (i = 0; i < ARRAY_SIZE(state->mbus_codes); i++) { if (format->format.code == state->mbus_codes[i]) { code = state->mbus_codes[i]; break; } } if (!code) code = state->mbus_codes[0]; tda1997x_fill_format(state, &format->format); format->format.code = code; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); *fmt = format->format; } else { int ret = tda1997x_setup_format(state, format->format.code); if (ret) return ret; /* mbus_code has changed - re-configure csc/vidout */ tda1997x_configure_csc(sd); tda1997x_configure_vidout(state); } return 0; } static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct tda1997x_state *state = to_state(sd); v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); memset(edid->reserved, 0, sizeof(edid->reserved)); if (edid->start_block == 0 && edid->blocks == 0) { edid->blocks = state->edid.blocks; return 0; } if (!state->edid.present) return -ENODATA; if (edid->start_block >= state->edid.blocks) return -EINVAL; if (edid->start_block + edid->blocks > state->edid.blocks) edid->blocks = state->edid.blocks - edid->start_block; memcpy(edid->edid, state->edid.edid + edid->start_block * 128, edid->blocks * 128); return 0; } static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct tda1997x_state *state = to_state(sd); int i; v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); memset(edid->reserved, 0, sizeof(edid->reserved)); if (edid->start_block != 0) return -EINVAL; if (edid->blocks == 0) { state->edid.blocks = 0; state->edid.present = 0; tda1997x_disable_edid(sd); return 0; } if (edid->blocks > 2) { edid->blocks = 2; return -E2BIG; } tda1997x_disable_edid(sd); /* write base EDID */ for (i = 0; i < 128; i++) io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]); /* write CEA Extension */ for (i = 0; i < 128; i++) io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); /* store state */ memcpy(state->edid.edid, edid->edid, 256); state->edid.blocks = edid->blocks; tda1997x_enable_edid(sd); return 0; } static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { *cap = tda1997x_dv_timings_cap; return 0; } static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap, NULL, NULL); } static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { .init_cfg = tda1997x_init_cfg, .enum_mbus_code = tda1997x_enum_mbus_code, .get_fmt = tda1997x_get_format, .set_fmt = tda1997x_set_format, .get_edid = tda1997x_get_edid, .set_edid = tda1997x_set_edid, .dv_timings_cap = tda1997x_get_dv_timings_cap, .enum_dv_timings = tda1997x_enum_dv_timings, }; /* ----------------------------------------------------------------------------- * v4l2_subdev_core_ops */ static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) { struct tda1997x_state *state = to_state(sd); union hdmi_infoframe frame; u8 buffer[40]; int len, err; /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", len, addr, buffer[0]); return err; } hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); return 0; } static int tda1997x_log_status(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); struct v4l2_dv_timings timings; struct hdmi_avi_infoframe *avi = &state->avi_infoframe; v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip: %s N%d\n", state->info->name, state->chip_revision + 1); v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no"); v4l2_info(sd, "-----Signal status-----\n"); v4l2_info(sd, "Cable detected (+5V power): %s\n", tda1997x_detect_tx_5v(sd) ? "yes" : "no"); v4l2_info(sd, "HPD detected: %s\n", tda1997x_detect_tx_hpd(sd) ? "yes" : "no"); v4l2_info(sd, "-----Video Timings-----\n"); switch (tda1997x_detect_std(state, &timings)) { case -ENOLINK: v4l2_info(sd, "No video detected\n"); break; case -ERANGE: v4l2_info(sd, "Invalid signal detected\n"); break; } v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, true); v4l2_info(sd, "-----Color space-----\n"); v4l2_info(sd, "Input color space: %s %s %s", hdmi_colorspace_names[avi->colorspace], (avi->colorspace == HDMI_COLORSPACE_RGB) ? "" : hdmi_colorimetry_names[avi->colorimetry], v4l2_quantization_names[state->colorimetry.quantization]); v4l2_info(sd, "Output color space: %s", vidfmt_names[state->vid_fmt]); v4l2_info(sd, "Color space conversion: %s", state->conv ? state->conv->name : "None"); v4l2_info(sd, "-----Audio-----\n"); if (state->audio_channels) { v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels, state->audio_samplerate); } else { v4l2_info(sd, "audio: none\n"); } v4l2_info(sd, "-----Infoframes-----\n"); tda1997x_log_infoframe(sd, AUD_IF); tda1997x_log_infoframe(sd, SPD_IF); tda1997x_log_infoframe(sd, AVI_IF); return 0; } static int tda1997x_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { switch (sub->type) { case V4L2_EVENT_SOURCE_CHANGE: return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); case V4L2_EVENT_CTRL: return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); default: return -EINVAL; } } static const struct v4l2_subdev_core_ops tda1997x_core_ops = { .log_status = tda1997x_log_status, .subscribe_event = tda1997x_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; /* ----------------------------------------------------------------------------- * v4l2_subdev_ops */ static const struct v4l2_subdev_ops tda1997x_subdev_ops = { .core = &tda1997x_core_ops, .video = &tda1997x_video_ops, .pad = &tda1997x_pad_ops, }; /* ----------------------------------------------------------------------------- * v4l2_controls */ static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct tda1997x_state *state = to_state(sd); switch (ctrl->id) { /* allow overriding the default RGB quantization range */ case V4L2_CID_DV_RX_RGB_RANGE: state->rgb_quantization_range = ctrl->val; set_rgb_quantization_range(state); tda1997x_configure_csc(sd); return 0; } return -EINVAL; }; static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct tda1997x_state *state = to_state(sd); if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) { ctrl->val = state->avi_infoframe.content_type; return 0; } return -EINVAL; }; static const struct v4l2_ctrl_ops tda1997x_ctrl_ops = { .s_ctrl = tda1997x_s_ctrl, .g_volatile_ctrl = tda1997x_g_volatile_ctrl, }; static int tda1997x_core_init(struct v4l2_subdev *sd) { struct tda1997x_state *state = to_state(sd); struct tda1997x_platform_data *pdata = &state->pdata; u8 reg; int i; /* disable HPD */ io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL); if (state->chip_revision == 0) { io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); } /* reset infoframe at end of start-up-sequencer */ io_write(sd, REG_SUS_SET_RGB2, 0x06); io_write(sd, REG_SUS_SET_RGB3, 0x06); /* Enable TMDS pull-ups */ io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT | RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A); /* enable sync measurement timing */ tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04); /* adjust CEC clock divider */ tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03); tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); io_write(sd, REG_TIMER_D, 0x54); /* enable power switch */ reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff); reg |= 0x20; tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg); mdelay(50); /* read the chip version */ reg = io_read(sd, REG_VERSION); /* get the chip configuration */ reg = io_read(sd, REG_CMTP_REG10); /* enable interrupts we care about */ io_write(sd, REG_INT_MASK_TOP, INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO | INTERRUPT_RATE | INTERRUPT_SUS); /* config_mtp,fmt,sus_end,sus_st */ io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END); /* rate stability change for inputs A/B */ io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST); /* aud,spd,avi*/ io_write(sd, REG_INT_MASK_INFO, MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF); /* audio_freq,audio_flg,mute_flg,fifo_err */ io_write(sd, REG_INT_MASK_AUDIO, MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG | MASK_ERROR_FIFO_PT); /* HDCP C5 state reached */ io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5); /* 5V detect and HDP pulse end */ io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V); /* don't care about AFE/MODE */ io_write(sd, REG_INT_MASK_AFE, 0); io_write(sd, REG_INT_MASK_MODE, 0); /* clear all interrupts */ io_write(sd, REG_INT_FLG_CLR_TOP, 0xff); io_write(sd, REG_INT_FLG_CLR_SUS, 0xff); io_write(sd, REG_INT_FLG_CLR_DDC, 0xff); io_write(sd, REG_INT_FLG_CLR_RATE, 0xff); io_write(sd, REG_INT_FLG_CLR_MODE, 0xff); io_write(sd, REG_INT_FLG_CLR_INFO, 0xff); io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff); io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff); io_write(sd, REG_INT_FLG_CLR_AFE, 0xff); /* init TMDS equalizer */ if (state->chip_revision == 0) io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE); io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE); if (state->chip_revision == 0) io_write(sd, REG_WDL_CFG, WDL_CFG_VAL); /* DC filter */ io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); /* disable test pattern */ io_write(sd, REG_SVC_MODE, 0x00); /* update HDMI INFO CTRL */ io_write(sd, REG_INFO_CTRL, 0xff); /* write HDMI INFO EXCEED value */ io_write(sd, REG_INFO_EXCEED, 3); if (state->chip_revision == 0) tda1997x_reset_n1(state); /* * No HDCP acknowledge when HDCP is disabled * and reset SUS to force format detection */ tda1997x_hdmi_info_reset(sd, NACK_HDCP, true); /* Set HPD low */ tda1997x_manual_hpd(sd, HPD_LOW_BP); /* Configure receiver capabilities */ io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); /* Configure HDMI: Auto HDCP mode, packet controlled mute */ reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT; reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT; io_write(sd, REG_HDMI_CTRL, reg); /* reset start-up-sequencer to force format detection */ tda1997x_hdmi_info_reset(sd, 0, true); /* disable matrix conversion */ reg = io_read(sd, REG_VDP_CTRL); reg |= VDP_CTRL_MATRIX_BP; io_write(sd, REG_VDP_CTRL, reg); /* set video output mode */ tda1997x_configure_vidout(state); /* configure video output port */ for (i = 0; i < 9; i++) { v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i, pdata->vidout_port_cfg[i]); io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]); } /* configure audio output port */ tda1997x_configure_audout(sd, 0); /* configure audio clock freq */ switch (pdata->audout_mclk_fs) { case 512: reg = AUDIO_CLOCK_SEL_512FS; break; case 256: reg = AUDIO_CLOCK_SEL_256FS; break; case 128: reg = AUDIO_CLOCK_SEL_128FS; break; case 64: reg = AUDIO_CLOCK_SEL_64FS; break; case 32: reg = AUDIO_CLOCK_SEL_32FS; break; default: reg = AUDIO_CLOCK_SEL_16FS; break; } io_write(sd, REG_AUDIO_CLOCK, reg); /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ tda1997x_hdmi_info_reset(sd, RESET_AI, false); /* reset infoframe */ tda1997x_hdmi_info_reset(sd, RESET_IF, false); /* reset audio infoframes */ tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); /* reset gamut */ tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false); /* get initial HDMI status */ state->hdmi_status = io_read(sd, REG_HDMI_FLAGS); return 0; } static int tda1997x_set_power(struct tda1997x_state *state, bool on) { int ret = 0; if (on) { ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES, state->supplies); msleep(300); } else { ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); } return ret; } static const struct i2c_device_id tda1997x_i2c_id[] = { {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, { }, }; MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); static const struct of_device_id tda1997x_of_id[] __maybe_unused = { { .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] }, { .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] }, { }, }; MODULE_DEVICE_TABLE(of, tda1997x_of_id); static int tda1997x_parse_dt(struct tda1997x_state *state) { struct tda1997x_platform_data *pdata = &state->pdata; struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *ep; struct device_node *np; unsigned int flags; const char *str; int ret; u32 v; /* * setup default values: * - HREF: active high from start to end of row * - VS: Vertical Sync active high at beginning of frame * - DE: Active high when data valid * - A_CLK: 128*Fs */ pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF; pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI; pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF; np = state->client->dev.of_node; ep = of_graph_get_next_endpoint(np, NULL); if (!ep) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); if (ret) { of_node_put(ep); return ret; } of_node_put(ep); pdata->vidout_bus_type = bus_cfg.bus_type; /* polarity of HS/VS/DE */ flags = bus_cfg.bus.parallel.flags; if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) pdata->vidout_inv_hs = 1; if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) pdata->vidout_inv_vs = 1; if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) pdata->vidout_inv_de = 1; pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width; /* video output port config */ ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg"); if (ret > 0) { u32 reg, val, i; for (i = 0; i < ret / 2 && i < 9; i++) { of_property_read_u32_index(np, "nxp,vidout-portcfg", i * 2, ®); of_property_read_u32_index(np, "nxp,vidout-portcfg", i * 2 + 1, &val); if (reg < 9) pdata->vidout_port_cfg[reg] = val; } } else { v4l_err(state->client, "nxp,vidout-portcfg missing\n"); return -EINVAL; } /* default to channel layout dictated by packet header */ pdata->audout_layoutauto = true; pdata->audout_format = AUDFMT_TYPE_DISABLED; if (!of_property_read_string(np, "nxp,audout-format", &str)) { if (strcmp(str, "i2s") == 0) pdata->audout_format = AUDFMT_TYPE_I2S; else if (strcmp(str, "spdif") == 0) pdata->audout_format = AUDFMT_TYPE_SPDIF; else { v4l_err(state->client, "nxp,audout-format invalid\n"); return -EINVAL; } if (!of_property_read_u32(np, "nxp,audout-layout", &v)) { switch (v) { case 0: case 1: break; default: v4l_err(state->client, "nxp,audout-layout invalid\n"); return -EINVAL; } pdata->audout_layout = v; } if (!of_property_read_u32(np, "nxp,audout-width", &v)) { switch (v) { case 16: case 32: break; default: v4l_err(state->client, "nxp,audout-width invalid\n"); return -EINVAL; } pdata->audout_width = v; } if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) { switch (v) { case 512: case 256: case 128: case 64: case 32: case 16: break; default: v4l_err(state->client, "nxp,audout-mclk-fs invalid\n"); return -EINVAL; } pdata->audout_mclk_fs = v; } } return 0; } static int tda1997x_get_regulators(struct tda1997x_state *state) { int i; for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++) state->supplies[i].supply = tda1997x_supply_name[i]; return devm_regulator_bulk_get(&state->client->dev, TDA1997X_NUM_SUPPLIES, state->supplies); } static int tda1997x_identify_module(struct tda1997x_state *state) { struct v4l2_subdev *sd = &state->sd; enum tda1997x_type type; u8 reg; /* Read chip configuration*/ reg = io_read(sd, REG_CMTP_REG10); state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ switch ((reg >> 4) & 0x03) { case 0x00: type = TDA19971; break; case 0x02: case 0x03: type = TDA19973; break; default: dev_err(&state->client->dev, "unsupported chip ID\n"); return -EIO; } if (state->info->type != type) { dev_err(&state->client->dev, "chip id mismatch\n"); return -EIO; } /* read chip revision */ state->chip_revision = io_read(sd, REG_CMTP_REG11); return 0; } static const struct media_entity_operations tda1997x_media_ops = { .link_validate = v4l2_subdev_link_validate, }; /* ----------------------------------------------------------------------------- * HDMI Audio Codec */ /* refine sample-rate based on HDMI source */ static int tda1997x_pcm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai); struct snd_soc_component *component = dai->component; struct snd_pcm_runtime *rtd = substream->runtime; int rate, err; rate = state->audio_samplerate; err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE, rate, rate); if (err < 0) { dev_err(component->dev, "failed to constrain samplerate to %dHz\n", rate); return err; } dev_info(component->dev, "set samplerate constraint to %dHz\n", rate); return 0; } static const struct snd_soc_dai_ops tda1997x_dai_ops = { .startup = tda1997x_pcm_startup, }; static struct snd_soc_dai_driver tda1997x_audio_dai = { .name = "tda1997x", .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, }, .ops = &tda1997x_dai_ops, }; static int tda1997x_codec_probe(struct snd_soc_component *component) { return 0; } static void tda1997x_codec_remove(struct snd_soc_component *component) { } static struct snd_soc_component_driver tda1997x_codec_driver = { .probe = tda1997x_codec_probe, .remove = tda1997x_codec_remove, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, .non_legacy_dai_naming = 1, }; static int tda1997x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tda1997x_state *state; struct tda1997x_platform_data *pdata; struct v4l2_subdev *sd; struct v4l2_ctrl_handler *hdl; struct v4l2_ctrl *ctrl; static const struct v4l2_dv_timings cea1920x1080 = V4L2_DV_BT_CEA_1920X1080P60; u32 *mbus_codes; int i, ret; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL); if (!state) return -ENOMEM; state->client = client; pdata = &state->pdata; if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { const struct of_device_id *oid; oid = of_match_node(tda1997x_of_id, client->dev.of_node); state->info = oid->data; ret = tda1997x_parse_dt(state); if (ret < 0) { v4l_err(client, "DT parsing error\n"); goto err_free_state; } } else if (client->dev.platform_data) { struct tda1997x_platform_data *pdata = client->dev.platform_data; state->info = (const struct tda1997x_chip_info *)id->driver_data; state->pdata = *pdata; } else { v4l_err(client, "No platform data\n"); ret = -ENODEV; goto err_free_state; } ret = tda1997x_get_regulators(state); if (ret) goto err_free_state; ret = tda1997x_set_power(state, 1); if (ret) goto err_free_state; mutex_init(&state->page_lock); mutex_init(&state->lock); state->page = 0xff; INIT_DELAYED_WORK(&state->delayed_work_enable_hpd, tda1997x_delayed_work_enable_hpd); /* set video format based on chip and bus width */ ret = tda1997x_identify_module(state); if (ret) goto err_free_mutex; /* initialize subdev */ sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_DV_DECODER; sd->entity.ops = &tda1997x_media_ops; /* set allowed mbus modes based on chip, bus-type, and bus-width */ i = 0; mbus_codes = state->mbus_codes; switch (state->info->type) { case TDA19973: switch (pdata->vidout_bus_type) { case V4L2_MBUS_PARALLEL: switch (pdata->vidout_bus_width) { case 36: mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36; mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36; /* fall-through */ case 24: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; break; } break; case V4L2_MBUS_BT656: switch (pdata->vidout_bus_width) { case 36: case 24: case 12: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; break; } break; default: break; } break; case TDA19971: switch (pdata->vidout_bus_type) { case V4L2_MBUS_PARALLEL: switch (pdata->vidout_bus_width) { case 24: mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24; mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24; mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; /* fall through */ case 20: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20; /* fall through */ case 16: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16; break; } break; case V4L2_MBUS_BT656: switch (pdata->vidout_bus_width) { case 24: case 20: case 16: case 12: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; /* fall through */ case 10: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; /* fall through */ case 8: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; break; } break; default: break; } break; } if (WARN_ON(i > ARRAY_SIZE(state->mbus_codes))) { ret = -EINVAL; goto err_free_mutex; } /* default format */ tda1997x_setup_format(state, state->mbus_codes[0]); state->timings = cea1920x1080; /* * default to SRGB full range quantization * (in case we don't get an infoframe such as DVI signal */ state->colorimetry.colorspace = V4L2_COLORSPACE_SRGB; state->colorimetry.quantization = V4L2_QUANTIZATION_FULL_RANGE; /* disable/reset HDCP to get correct I2C access to Rx HDMI */ io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); /* * if N2 version, reset compdel_bp as it may generate some small pixel * shifts in case of embedded sync/or delay lower than 4 */ if (state->chip_revision != 0) { io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00); io_write(sd, REG_VDP_CTRL, 0x1f); } v4l_info(client, "NXP %s N%d detected\n", state->info->name, state->chip_revision + 1); v4l_info(client, "video: %dbit %s %d formats available\n", pdata->vidout_bus_width, (pdata->vidout_bus_type == V4L2_MBUS_PARALLEL) ? "parallel" : "BT656", i); if (pdata->audout_format) { v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n", pdata->audout_layout ? 2 : 8, audfmt_names[pdata->audout_format], pdata->audout_layout, pdata->audout_mclk_fs); } ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03); state->client_cec = i2c_new_dummy(client->adapter, ret); v4l_info(client, "CEC slave address 0x%02x\n", ret); ret = tda1997x_core_init(sd); if (ret) goto err_free_mutex; /* control handlers */ hdl = &state->hdl; v4l2_ctrl_handler_init(hdl, 3); ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops, V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; /* custom controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); state->sd.ctrl_handler = hdl; if (hdl->error) { ret = hdl->error; goto err_free_handler; } v4l2_ctrl_handler_setup(hdl); /* initialize source pads */ state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS, state->pads); if (ret) { v4l_err(client, "failed entity_init: %d", ret); goto err_free_handler; } ret = v4l2_async_register_subdev(sd); if (ret) goto err_free_media; /* register audio DAI */ if (pdata->audout_format) { u64 formats; if (pdata->audout_width == 32) formats = SNDRV_PCM_FMTBIT_S32_LE; else formats = SNDRV_PCM_FMTBIT_S16_LE; tda1997x_audio_dai.capture.formats = formats; ret = devm_snd_soc_register_component(&state->client->dev, &tda1997x_codec_driver, &tda1997x_audio_dai, 1); if (ret) { dev_err(&client->dev, "register audio codec failed\n"); goto err_free_media; } dev_set_drvdata(&state->client->dev, state); v4l_info(state->client, "registered audio codec\n"); } /* request irq */ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, tda1997x_isr_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, KBUILD_MODNAME, state); if (ret) { v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret); goto err_free_media; } return 0; err_free_media: media_entity_cleanup(&sd->entity); err_free_handler: v4l2_ctrl_handler_free(&state->hdl); err_free_mutex: cancel_delayed_work(&state->delayed_work_enable_hpd); mutex_destroy(&state->page_lock); mutex_destroy(&state->lock); err_free_state: kfree(state); dev_err(&client->dev, "%s failed: %d\n", __func__, ret); return ret; } static int tda1997x_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct tda1997x_state *state = to_state(sd); struct tda1997x_platform_data *pdata = &state->pdata; if (pdata->audout_format) { mutex_destroy(&state->audio_lock); } disable_irq(state->client->irq); tda1997x_power_mode(state, 0); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&state->hdl); regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); i2c_unregister_device(state->client_cec); cancel_delayed_work(&state->delayed_work_enable_hpd); mutex_destroy(&state->page_lock); mutex_destroy(&state->lock); kfree(state); return 0; } static struct i2c_driver tda1997x_i2c_driver = { .driver = { .name = "tda1997x", .of_match_table = of_match_ptr(tda1997x_of_id), }, .probe = tda1997x_probe, .remove = tda1997x_remove, .id_table = tda1997x_i2c_id, }; module_i2c_driver(tda1997x_i2c_driver); MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); MODULE_DESCRIPTION("TDA1997X HDMI Receiver driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1