Message ID | 1484897930-1275-20-git-send-email-a.hajda@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 01/20/2017 01:08 PM, Andrzej Hajda wrote: > MHL3 protocol requires registry adjustments depending on chosen video mode. > Necessary information is gathered in mode_fixup callback. In case of HDMI > video modes driver should also send special AVI and MHL infoframes. > > Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> > --- > drivers/gpu/drm/bridge/sil-sii8620.c | 249 +++++++++++++++++++++++++++++++---- > drivers/gpu/drm/bridge/sil-sii8620.h | 15 ++- > 2 files changed, 231 insertions(+), 33 deletions(-) > > diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c > index 1c76905..2c7b5b9 100644 > --- a/drivers/gpu/drm/bridge/sil-sii8620.c > +++ b/drivers/gpu/drm/bridge/sil-sii8620.c > @@ -32,6 +32,8 @@ > > #define SII8620_BURST_BUF_LEN 288 > #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) > +#define MHL1_MAX_LCLK 225000 > +#define MHL3_MAX_LCLK 600000 > > enum sii8620_mode { > CM_DISCONNECTED, > @@ -62,6 +64,9 @@ struct sii8620 { > struct regulator_bulk_data supplies[2]; > struct mutex lock; /* context lock, protects fields below */ > int error; > + int pixel_clock; > + unsigned int use_packed_pixel:1; > + int video_code; > enum sii8620_mode mode; > enum sii8620_sink_type sink_type; > u8 cbus_status; > @@ -69,7 +74,7 @@ struct sii8620 { > u8 xstat[MHL_XDS_SIZE]; > u8 devcap[MHL_DCAP_SIZE]; > u8 xdevcap[MHL_XDC_SIZE]; > - u8 avif[19]; > + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; > struct edid *edid; > unsigned int gen2_write_burst:1; > enum sii8620_mt_state mt_state; > @@ -685,6 +690,40 @@ static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) > d->size = cpu_to_le16(size); > } > > +static u8 sii8620_checksum(void *ptr, int size) > +{ > + u8 *d = ptr, sum = 0; > + > + while (size--) > + sum += *d++; > + > + return sum; > +} > + > +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, > + enum mhl_burst_id id) > +{ > + h->id = cpu_to_be16(id); > + h->total_entries = 1; > + h->sequence_index = 1; > +} > + > +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) > +{ > + struct mhl_burst_bits_per_pixel_fmt *d; > + const int size = sizeof(*d) + sizeof(d->desc[0]); > + > + d = sii8620_burst_get_tx_buf(ctx, size); > + if (!d) > + return; > + > + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); > + d->num_entries = 1; > + d->desc[0].stream_id = 0; > + d->desc[0].pixel_format = fmt; > + d->hdr.checksum -= sii8620_checksum(d, size); > +} > + > static void sii8620_burst_rx_all(struct sii8620 *ctx) > { > u8 *d = ctx->burst.rx_buf; > @@ -949,32 +988,162 @@ static void sii8620_stop_video(struct sii8620 *ctx) > sii8620_write(ctx, REG_TPI_SC, val); > } > > +static void sii8620_set_format(struct sii8620 *ctx) > +{ > + u8 out_fmt; > + > + if (sii8620_is_mhl3(ctx)) { > + sii8620_setbits(ctx, REG_M3_P0CTRL, > + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, > + ctx->use_packed_pixel ? ~0 : 0); > + } else { > + if (ctx->use_packed_pixel) > + sii8620_write_seq_static(ctx, > + REG_VID_MODE, BIT_VID_MODE_M1080P, > + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, > + REG_MHLTX_CTL6, 0x60 > + ); > + else > + sii8620_write_seq_static(ctx, > + REG_VID_MODE, 0, > + REG_MHL_TOP_CTL, 1, > + REG_MHLTX_CTL6, 0xa0 > + ); > + } > + > + if (ctx->use_packed_pixel) > + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | > + BIT_TPI_OUTPUT_CSCMODE709; > + else > + out_fmt = VAL_TPI_FORMAT(RGB, FULL); > + > + sii8620_write_seq(ctx, > + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), > + REG_TPI_OUTPUT, out_fmt, > + ); > +} > + > +#define MHL3_VSIF_TYPE 0x81 > +#define MHL3_VSIF_VERSION 0x03 > +#define MHL3_VSIF_LENGTH 0x0f > +#define IEEE_OUI_MHL3 0x7CA61D Should these be in mhl.h? Some of these seem similar to the infoframe related defs in include/linux/hdmi.h Do you think there should be equivalent helpers for mhl infoframes similar to hdmi_vendor_infoframe_init and hdmi_vendor_infoframe_pack()? Thanks, Archit > + > +static void sii8620_mhl_infoframe_init(void *ptr, int size) > +{ > + u8 *buf = ptr; > + > + memset(buf, 0, size); > + buf[0] = MHL3_VSIF_TYPE; > + buf[1] = MHL3_VSIF_VERSION; > + buf[2] = MHL3_VSIF_LENGTH; > + buf[4] = IEEE_OUI_MHL3 & 0xff; > + buf[5] = (IEEE_OUI_MHL3 >> 8) & 0xff; > + buf[6] = (IEEE_OUI_MHL3 >> 16) & 0xff; > + buf[3] -= sii8620_checksum(buf, MHL3_VSIF_LENGTH); > +} > + > +static void sii8620_set_infoframes(struct sii8620 *ctx) > +{ > + union hdmi_infoframe frm; > + u8 buf[31]; > + int ret; > + > + if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { > + sii8620_write(ctx, REG_TPI_SC, > + BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); > + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3, > + ARRAY_SIZE(ctx->avif) - 3); > + sii8620_write(ctx, REG_PKT_FILTER_0, > + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | > + BIT_PKT_FILTER_0_DROP_MPEG_PKT | > + BIT_PKT_FILTER_0_DROP_GCP_PKT, > + BIT_PKT_FILTER_1_DROP_GEN_PKT); > + return; > + } > + > + ret = hdmi_avi_infoframe_init(&frm.avi); > + frm.avi.colorspace = HDMI_COLORSPACE_YUV422; > + frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; > + frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9; > + frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709; > + frm.avi.video_code = ctx->video_code; > + if (!ret) > + ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); > + if (ret > 0) > + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); > + sii8620_write(ctx, REG_PKT_FILTER_0, > + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | > + BIT_PKT_FILTER_0_DROP_MPEG_PKT | > + BIT_PKT_FILTER_0_DROP_AVI_PKT | > + BIT_PKT_FILTER_0_DROP_GCP_PKT, > + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | > + BIT_PKT_FILTER_1_DROP_GEN_PKT | > + BIT_PKT_FILTER_1_DROP_VSIF_PKT); > + > + sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN > + | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); > + sii8620_mhl_infoframe_init(buf, ARRAY_SIZE(buf)); > + sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ARRAY_SIZE(buf)); > +} > + > static void sii8620_start_hdmi(struct sii8620 *ctx) > { > sii8620_write_seq_static(ctx, > REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL > | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, > REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE > - | BIT_VID_OVRRD_M1080P_OVRRD, > - REG_VID_MODE, 0, > - REG_MHL_TOP_CTL, 0x1, > - REG_MHLTX_CTL6, 0xa0, > - REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), > - REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), > - ); > + | BIT_VID_OVRRD_M1080P_OVRRD); > + sii8620_set_format(ctx); > > - sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), > - MHL_DST_LM_CLK_MODE_NORMAL | > - MHL_DST_LM_PATH_ENABLED); > - > - sii8620_set_auto_zone(ctx); > + if (!sii8620_is_mhl3(ctx)) { > + sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), > + MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); > + sii8620_set_auto_zone(ctx); > + } else { > + static const struct { > + int max_clk; > + u8 zone; > + u8 link_rate; > + u8 rrp_decode; > + } clk_spec[] = { > + { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, > + MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, > + { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, > + MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, > + { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, > + MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, > + }; > + u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; > + int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); > + int i; > + > + for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) > + if (clk < clk_spec[i].max_clk) > + break; > > - sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); > + if (100 * clk >= 98 * clk_spec[i].max_clk) > + p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; > > - sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif, > - ARRAY_SIZE(ctx->avif)); > + sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); > + sii8620_burst_send(ctx); > + sii8620_write_seq(ctx, > + REG_MHL_DP_CTL0, 0xf0, > + REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); > + sii8620_setbits(ctx, REG_M3_P0CTRL, > + BIT_M3_P0CTRL_MHL3_P0_PORT_EN > + | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); > + sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, > + clk_spec[i].rrp_decode); > + sii8620_write_seq_static(ctx, > + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE > + | BIT_M3_CTRL_H2M_SWRST, > + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE > + ); > + sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), > + clk_spec[i].link_rate); > + } > > - sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2); > + sii8620_set_infoframes(ctx); > } > > static void sii8620_start_video(struct sii8620 *ctx) > @@ -1834,22 +2003,44 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, > struct drm_display_mode *adjusted_mode) > { > struct sii8620 *ctx = bridge_to_sii8620(bridge); > - bool ret = false; > - int max_clock = 74250; > - > - mutex_lock(&ctx->lock); > - > - if (mode->flags & DRM_MODE_FLAG_INTERLACE) > - goto out; > + int max_lclk; > + bool ret = true; > > - if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) > - max_clock = 300000; > + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) > + return false; > > - ret = mode->clock <= max_clock; > + mutex_lock(&ctx->lock); > > -out: > + max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; > + if (max_lclk > 3 * adjusted_mode->clock) { > + ctx->use_packed_pixel = 0; > + goto end; > + } > + if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && > + max_lclk > 2 * adjusted_mode->clock) { > + ctx->use_packed_pixel = 1; > + goto end; > + } > + ret = false; > +end: > + if (ret) { > + u8 vic = drm_match_cea_mode(adjusted_mode); > + > + if (!vic) { > + union hdmi_infoframe frm; > + u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; > + > + drm_hdmi_vendor_infoframe_from_display_mode( > + &frm.vendor.hdmi, adjusted_mode); > + vic = frm.vendor.hdmi.vic; > + if (vic >= ARRAY_SIZE(mhl_vic)) > + vic = 0; > + vic = mhl_vic[vic]; > + } > + ctx->video_code = vic; > + ctx->pixel_clock = adjusted_mode->clock; > + } > mutex_unlock(&ctx->lock); > - > return ret; > } > > diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h > index f7bfbc3..aefae67 100644 > --- a/drivers/gpu/drm/bridge/sil-sii8620.h > +++ b/drivers/gpu/drm/bridge/sil-sii8620.h > @@ -1084,10 +1084,17 @@ > > /* TPI Info Frame Select, default value: 0x00 */ > #define REG_TPI_INFO_FSEL 0x06bf > -#define BIT_TPI_INFO_FSEL_TPI_INFO_EN BIT(7) > -#define BIT_TPI_INFO_FSEL_TPI_INFO_RPT BIT(6) > -#define BIT_TPI_INFO_FSEL_TPI_INFO_READ_FLAG BIT(5) > -#define MSK_TPI_INFO_FSEL_TPI_INFO_SEL 0x07 > +#define BIT_TPI_INFO_FSEL_EN BIT(7) > +#define BIT_TPI_INFO_FSEL_RPT BIT(6) > +#define BIT_TPI_INFO_FSEL_READ_FLAG BIT(5) > +#define MSK_TPI_INFO_FSEL_PKT 0x07 > +#define VAL_TPI_INFO_FSEL_AVI 0x00 > +#define VAL_TPI_INFO_FSEL_SPD 0x01 > +#define VAL_TPI_INFO_FSEL_AUD 0x02 > +#define VAL_TPI_INFO_FSEL_MPG 0x03 > +#define VAL_TPI_INFO_FSEL_GEN 0x04 > +#define VAL_TPI_INFO_FSEL_GEN2 0x05 > +#define VAL_TPI_INFO_FSEL_VSI 0x06 > > /* TPI Info Byte #0, default value: 0x00 */ > #define REG_TPI_INFO_B0 0x06c0 >
On 23.01.2017 10:17, Archit Taneja wrote: > > On 01/20/2017 01:08 PM, Andrzej Hajda wrote: >> MHL3 protocol requires registry adjustments depending on chosen video mode. >> Necessary information is gathered in mode_fixup callback. In case of HDMI >> video modes driver should also send special AVI and MHL infoframes. >> >> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> >> --- >> drivers/gpu/drm/bridge/sil-sii8620.c | 249 +++++++++++++++++++++++++++++++---- >> drivers/gpu/drm/bridge/sil-sii8620.h | 15 ++- >> 2 files changed, 231 insertions(+), 33 deletions(-) >> >> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c >> index 1c76905..2c7b5b9 100644 >> --- a/drivers/gpu/drm/bridge/sil-sii8620.c >> +++ b/drivers/gpu/drm/bridge/sil-sii8620.c >> @@ -32,6 +32,8 @@ >> >> #define SII8620_BURST_BUF_LEN 288 >> #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) >> +#define MHL1_MAX_LCLK 225000 >> +#define MHL3_MAX_LCLK 600000 >> >> enum sii8620_mode { >> CM_DISCONNECTED, >> @@ -62,6 +64,9 @@ struct sii8620 { >> struct regulator_bulk_data supplies[2]; >> struct mutex lock; /* context lock, protects fields below */ >> int error; >> + int pixel_clock; >> + unsigned int use_packed_pixel:1; >> + int video_code; >> enum sii8620_mode mode; >> enum sii8620_sink_type sink_type; >> u8 cbus_status; >> @@ -69,7 +74,7 @@ struct sii8620 { >> u8 xstat[MHL_XDS_SIZE]; >> u8 devcap[MHL_DCAP_SIZE]; >> u8 xdevcap[MHL_XDC_SIZE]; >> - u8 avif[19]; >> + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; >> struct edid *edid; >> unsigned int gen2_write_burst:1; >> enum sii8620_mt_state mt_state; >> @@ -685,6 +690,40 @@ static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) >> d->size = cpu_to_le16(size); >> } >> >> +static u8 sii8620_checksum(void *ptr, int size) >> +{ >> + u8 *d = ptr, sum = 0; >> + >> + while (size--) >> + sum += *d++; >> + >> + return sum; >> +} >> + >> +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, >> + enum mhl_burst_id id) >> +{ >> + h->id = cpu_to_be16(id); >> + h->total_entries = 1; >> + h->sequence_index = 1; >> +} >> + >> +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) >> +{ >> + struct mhl_burst_bits_per_pixel_fmt *d; >> + const int size = sizeof(*d) + sizeof(d->desc[0]); >> + >> + d = sii8620_burst_get_tx_buf(ctx, size); >> + if (!d) >> + return; >> + >> + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); >> + d->num_entries = 1; >> + d->desc[0].stream_id = 0; >> + d->desc[0].pixel_format = fmt; >> + d->hdr.checksum -= sii8620_checksum(d, size); >> +} >> + >> static void sii8620_burst_rx_all(struct sii8620 *ctx) >> { >> u8 *d = ctx->burst.rx_buf; >> @@ -949,32 +988,162 @@ static void sii8620_stop_video(struct sii8620 *ctx) >> sii8620_write(ctx, REG_TPI_SC, val); >> } >> >> +static void sii8620_set_format(struct sii8620 *ctx) >> +{ >> + u8 out_fmt; >> + >> + if (sii8620_is_mhl3(ctx)) { >> + sii8620_setbits(ctx, REG_M3_P0CTRL, >> + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, >> + ctx->use_packed_pixel ? ~0 : 0); >> + } else { >> + if (ctx->use_packed_pixel) >> + sii8620_write_seq_static(ctx, >> + REG_VID_MODE, BIT_VID_MODE_M1080P, >> + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, >> + REG_MHLTX_CTL6, 0x60 >> + ); >> + else >> + sii8620_write_seq_static(ctx, >> + REG_VID_MODE, 0, >> + REG_MHL_TOP_CTL, 1, >> + REG_MHLTX_CTL6, 0xa0 >> + ); >> + } >> + >> + if (ctx->use_packed_pixel) >> + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | >> + BIT_TPI_OUTPUT_CSCMODE709; >> + else >> + out_fmt = VAL_TPI_FORMAT(RGB, FULL); >> + >> + sii8620_write_seq(ctx, >> + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), >> + REG_TPI_OUTPUT, out_fmt, >> + ); >> +} >> + >> +#define MHL3_VSIF_TYPE 0x81 >> +#define MHL3_VSIF_VERSION 0x03 >> +#define MHL3_VSIF_LENGTH 0x0f >> +#define IEEE_OUI_MHL3 0x7CA61D > Should these be in mhl.h? > > Some of these seem similar to the infoframe related defs > in include/linux/hdmi.h > > Do you think there should be equivalent helpers for mhl infoframes > similar to hdmi_vendor_infoframe_init > and hdmi_vendor_infoframe_pack()? Answer is yes, IMHO, for both questions, but since the only documentation I have access to is vendor code I am little bit hesitant in pushing such things into mhl header. I am not sure if I can properly describe all fields of MHL3 VSIF infoframe, but beside this I have no objections :) One thing is in case of implementing mhl infoframe helpers: where to put them to, ie which file? Regards Andrzej
On 01/23/2017 04:42 PM, Andrzej Hajda wrote: > On 23.01.2017 10:17, Archit Taneja wrote: >> >> On 01/20/2017 01:08 PM, Andrzej Hajda wrote: >>> MHL3 protocol requires registry adjustments depending on chosen video mode. >>> Necessary information is gathered in mode_fixup callback. In case of HDMI >>> video modes driver should also send special AVI and MHL infoframes. >>> >>> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> >>> --- >>> drivers/gpu/drm/bridge/sil-sii8620.c | 249 +++++++++++++++++++++++++++++++---- >>> drivers/gpu/drm/bridge/sil-sii8620.h | 15 ++- >>> 2 files changed, 231 insertions(+), 33 deletions(-) >>> >>> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c >>> index 1c76905..2c7b5b9 100644 >>> --- a/drivers/gpu/drm/bridge/sil-sii8620.c >>> +++ b/drivers/gpu/drm/bridge/sil-sii8620.c >>> @@ -32,6 +32,8 @@ >>> >>> #define SII8620_BURST_BUF_LEN 288 >>> #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) >>> +#define MHL1_MAX_LCLK 225000 >>> +#define MHL3_MAX_LCLK 600000 >>> >>> enum sii8620_mode { >>> CM_DISCONNECTED, >>> @@ -62,6 +64,9 @@ struct sii8620 { >>> struct regulator_bulk_data supplies[2]; >>> struct mutex lock; /* context lock, protects fields below */ >>> int error; >>> + int pixel_clock; >>> + unsigned int use_packed_pixel:1; >>> + int video_code; >>> enum sii8620_mode mode; >>> enum sii8620_sink_type sink_type; >>> u8 cbus_status; >>> @@ -69,7 +74,7 @@ struct sii8620 { >>> u8 xstat[MHL_XDS_SIZE]; >>> u8 devcap[MHL_DCAP_SIZE]; >>> u8 xdevcap[MHL_XDC_SIZE]; >>> - u8 avif[19]; >>> + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; >>> struct edid *edid; >>> unsigned int gen2_write_burst:1; >>> enum sii8620_mt_state mt_state; >>> @@ -685,6 +690,40 @@ static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) >>> d->size = cpu_to_le16(size); >>> } >>> >>> +static u8 sii8620_checksum(void *ptr, int size) >>> +{ >>> + u8 *d = ptr, sum = 0; >>> + >>> + while (size--) >>> + sum += *d++; >>> + >>> + return sum; >>> +} >>> + >>> +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, >>> + enum mhl_burst_id id) >>> +{ >>> + h->id = cpu_to_be16(id); >>> + h->total_entries = 1; >>> + h->sequence_index = 1; >>> +} >>> + >>> +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) >>> +{ >>> + struct mhl_burst_bits_per_pixel_fmt *d; >>> + const int size = sizeof(*d) + sizeof(d->desc[0]); >>> + >>> + d = sii8620_burst_get_tx_buf(ctx, size); >>> + if (!d) >>> + return; >>> + >>> + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); >>> + d->num_entries = 1; >>> + d->desc[0].stream_id = 0; >>> + d->desc[0].pixel_format = fmt; >>> + d->hdr.checksum -= sii8620_checksum(d, size); >>> +} >>> + >>> static void sii8620_burst_rx_all(struct sii8620 *ctx) >>> { >>> u8 *d = ctx->burst.rx_buf; >>> @@ -949,32 +988,162 @@ static void sii8620_stop_video(struct sii8620 *ctx) >>> sii8620_write(ctx, REG_TPI_SC, val); >>> } >>> >>> +static void sii8620_set_format(struct sii8620 *ctx) >>> +{ >>> + u8 out_fmt; >>> + >>> + if (sii8620_is_mhl3(ctx)) { >>> + sii8620_setbits(ctx, REG_M3_P0CTRL, >>> + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, >>> + ctx->use_packed_pixel ? ~0 : 0); >>> + } else { >>> + if (ctx->use_packed_pixel) >>> + sii8620_write_seq_static(ctx, >>> + REG_VID_MODE, BIT_VID_MODE_M1080P, >>> + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, >>> + REG_MHLTX_CTL6, 0x60 >>> + ); >>> + else >>> + sii8620_write_seq_static(ctx, >>> + REG_VID_MODE, 0, >>> + REG_MHL_TOP_CTL, 1, >>> + REG_MHLTX_CTL6, 0xa0 >>> + ); >>> + } >>> + >>> + if (ctx->use_packed_pixel) >>> + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | >>> + BIT_TPI_OUTPUT_CSCMODE709; >>> + else >>> + out_fmt = VAL_TPI_FORMAT(RGB, FULL); >>> + >>> + sii8620_write_seq(ctx, >>> + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), >>> + REG_TPI_OUTPUT, out_fmt, >>> + ); >>> +} >>> + >>> +#define MHL3_VSIF_TYPE 0x81 >>> +#define MHL3_VSIF_VERSION 0x03 >>> +#define MHL3_VSIF_LENGTH 0x0f >>> +#define IEEE_OUI_MHL3 0x7CA61D >> Should these be in mhl.h? >> >> Some of these seem similar to the infoframe related defs >> in include/linux/hdmi.h >> >> Do you think there should be equivalent helpers for mhl infoframes >> similar to hdmi_vendor_infoframe_init >> and hdmi_vendor_infoframe_pack()? > > Answer is yes, IMHO, for both questions, but since the only > documentation I have access to is vendor code I am little bit hesitant > in pushing such things into mhl header. > I am not sure if I can properly describe all fields of MHL3 VSIF > infoframe, but beside this I have no objections :) I think it should be okay to move these in mhl.h. I think we should drop the version macro, though. The hdmi helpers don't created macros for the infoframe numbers either. > One thing is in case of implementing mhl infoframe helpers: where to put > them to, ie which file? I guess we could eventually have a new mhl helper, similar to drivers/video/hdmi.c. The mhl helper probably could sit in the bridge folder for now, since I have only heard of MHL being implemented as an external bridge chip. For now, you could maybe add a comment saying that these funcs can potentially be helpers, and give them non-sil8620 name. Thanks, Archit
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 1c76905..2c7b5b9 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -32,6 +32,8 @@ #define SII8620_BURST_BUF_LEN 288 #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) +#define MHL1_MAX_LCLK 225000 +#define MHL3_MAX_LCLK 600000 enum sii8620_mode { CM_DISCONNECTED, @@ -62,6 +64,9 @@ struct sii8620 { struct regulator_bulk_data supplies[2]; struct mutex lock; /* context lock, protects fields below */ int error; + int pixel_clock; + unsigned int use_packed_pixel:1; + int video_code; enum sii8620_mode mode; enum sii8620_sink_type sink_type; u8 cbus_status; @@ -69,7 +74,7 @@ struct sii8620 { u8 xstat[MHL_XDS_SIZE]; u8 devcap[MHL_DCAP_SIZE]; u8 xdevcap[MHL_XDC_SIZE]; - u8 avif[19]; + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; struct edid *edid; unsigned int gen2_write_burst:1; enum sii8620_mt_state mt_state; @@ -685,6 +690,40 @@ static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) d->size = cpu_to_le16(size); } +static u8 sii8620_checksum(void *ptr, int size) +{ + u8 *d = ptr, sum = 0; + + while (size--) + sum += *d++; + + return sum; +} + +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, + enum mhl_burst_id id) +{ + h->id = cpu_to_be16(id); + h->total_entries = 1; + h->sequence_index = 1; +} + +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) +{ + struct mhl_burst_bits_per_pixel_fmt *d; + const int size = sizeof(*d) + sizeof(d->desc[0]); + + d = sii8620_burst_get_tx_buf(ctx, size); + if (!d) + return; + + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); + d->num_entries = 1; + d->desc[0].stream_id = 0; + d->desc[0].pixel_format = fmt; + d->hdr.checksum -= sii8620_checksum(d, size); +} + static void sii8620_burst_rx_all(struct sii8620 *ctx) { u8 *d = ctx->burst.rx_buf; @@ -949,32 +988,162 @@ static void sii8620_stop_video(struct sii8620 *ctx) sii8620_write(ctx, REG_TPI_SC, val); } +static void sii8620_set_format(struct sii8620 *ctx) +{ + u8 out_fmt; + + if (sii8620_is_mhl3(ctx)) { + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, + ctx->use_packed_pixel ? ~0 : 0); + } else { + if (ctx->use_packed_pixel) + sii8620_write_seq_static(ctx, + REG_VID_MODE, BIT_VID_MODE_M1080P, + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, + REG_MHLTX_CTL6, 0x60 + ); + else + sii8620_write_seq_static(ctx, + REG_VID_MODE, 0, + REG_MHL_TOP_CTL, 1, + REG_MHLTX_CTL6, 0xa0 + ); + } + + if (ctx->use_packed_pixel) + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | + BIT_TPI_OUTPUT_CSCMODE709; + else + out_fmt = VAL_TPI_FORMAT(RGB, FULL); + + sii8620_write_seq(ctx, + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), + REG_TPI_OUTPUT, out_fmt, + ); +} + +#define MHL3_VSIF_TYPE 0x81 +#define MHL3_VSIF_VERSION 0x03 +#define MHL3_VSIF_LENGTH 0x0f +#define IEEE_OUI_MHL3 0x7CA61D + +static void sii8620_mhl_infoframe_init(void *ptr, int size) +{ + u8 *buf = ptr; + + memset(buf, 0, size); + buf[0] = MHL3_VSIF_TYPE; + buf[1] = MHL3_VSIF_VERSION; + buf[2] = MHL3_VSIF_LENGTH; + buf[4] = IEEE_OUI_MHL3 & 0xff; + buf[5] = (IEEE_OUI_MHL3 >> 8) & 0xff; + buf[6] = (IEEE_OUI_MHL3 >> 16) & 0xff; + buf[3] -= sii8620_checksum(buf, MHL3_VSIF_LENGTH); +} + +static void sii8620_set_infoframes(struct sii8620 *ctx) +{ + union hdmi_infoframe frm; + u8 buf[31]; + int ret; + + if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { + sii8620_write(ctx, REG_TPI_SC, + BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3, + ARRAY_SIZE(ctx->avif) - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_DROP_GEN_PKT); + return; + } + + ret = hdmi_avi_infoframe_init(&frm.avi); + frm.avi.colorspace = HDMI_COLORSPACE_YUV422; + frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9; + frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709; + frm.avi.video_code = ctx->video_code; + if (!ret) + ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); + if (ret > 0) + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_AVI_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | + BIT_PKT_FILTER_1_DROP_GEN_PKT | + BIT_PKT_FILTER_1_DROP_VSIF_PKT); + + sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN + | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); + sii8620_mhl_infoframe_init(buf, ARRAY_SIZE(buf)); + sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ARRAY_SIZE(buf)); +} + static void sii8620_start_hdmi(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE - | BIT_VID_OVRRD_M1080P_OVRRD, - REG_VID_MODE, 0, - REG_MHL_TOP_CTL, 0x1, - REG_MHLTX_CTL6, 0xa0, - REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), - REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), - ); + | BIT_VID_OVRRD_M1080P_OVRRD); + sii8620_set_format(ctx); - sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), - MHL_DST_LM_CLK_MODE_NORMAL | - MHL_DST_LM_PATH_ENABLED); - - sii8620_set_auto_zone(ctx); + if (!sii8620_is_mhl3(ctx)) { + sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), + MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); + sii8620_set_auto_zone(ctx); + } else { + static const struct { + int max_clk; + u8 zone; + u8 link_rate; + u8 rrp_decode; + } clk_spec[] = { + { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, + MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, + { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, + MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, + { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, + MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, + }; + u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; + int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); + int i; + + for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) + if (clk < clk_spec[i].max_clk) + break; - sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + if (100 * clk >= 98 * clk_spec[i].max_clk) + p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; - sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif, - ARRAY_SIZE(ctx->avif)); + sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); + sii8620_burst_send(ctx); + sii8620_write_seq(ctx, + REG_MHL_DP_CTL0, 0xf0, + REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN + | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); + sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, + clk_spec[i].rrp_decode); + sii8620_write_seq_static(ctx, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + | BIT_M3_CTRL_H2M_SWRST, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + ); + sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), + clk_spec[i].link_rate); + } - sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2); + sii8620_set_infoframes(ctx); } static void sii8620_start_video(struct sii8620 *ctx) @@ -1834,22 +2003,44 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); - bool ret = false; - int max_clock = 74250; - - mutex_lock(&ctx->lock); - - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - goto out; + int max_lclk; + bool ret = true; - if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) - max_clock = 300000; + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + return false; - ret = mode->clock <= max_clock; + mutex_lock(&ctx->lock); -out: + max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; + if (max_lclk > 3 * adjusted_mode->clock) { + ctx->use_packed_pixel = 0; + goto end; + } + if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && + max_lclk > 2 * adjusted_mode->clock) { + ctx->use_packed_pixel = 1; + goto end; + } + ret = false; +end: + if (ret) { + u8 vic = drm_match_cea_mode(adjusted_mode); + + if (!vic) { + union hdmi_infoframe frm; + u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; + + drm_hdmi_vendor_infoframe_from_display_mode( + &frm.vendor.hdmi, adjusted_mode); + vic = frm.vendor.hdmi.vic; + if (vic >= ARRAY_SIZE(mhl_vic)) + vic = 0; + vic = mhl_vic[vic]; + } + ctx->video_code = vic; + ctx->pixel_clock = adjusted_mode->clock; + } mutex_unlock(&ctx->lock); - return ret; } diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index f7bfbc3..aefae67 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -1084,10 +1084,17 @@ /* TPI Info Frame Select, default value: 0x00 */ #define REG_TPI_INFO_FSEL 0x06bf -#define BIT_TPI_INFO_FSEL_TPI_INFO_EN BIT(7) -#define BIT_TPI_INFO_FSEL_TPI_INFO_RPT BIT(6) -#define BIT_TPI_INFO_FSEL_TPI_INFO_READ_FLAG BIT(5) -#define MSK_TPI_INFO_FSEL_TPI_INFO_SEL 0x07 +#define BIT_TPI_INFO_FSEL_EN BIT(7) +#define BIT_TPI_INFO_FSEL_RPT BIT(6) +#define BIT_TPI_INFO_FSEL_READ_FLAG BIT(5) +#define MSK_TPI_INFO_FSEL_PKT 0x07 +#define VAL_TPI_INFO_FSEL_AVI 0x00 +#define VAL_TPI_INFO_FSEL_SPD 0x01 +#define VAL_TPI_INFO_FSEL_AUD 0x02 +#define VAL_TPI_INFO_FSEL_MPG 0x03 +#define VAL_TPI_INFO_FSEL_GEN 0x04 +#define VAL_TPI_INFO_FSEL_GEN2 0x05 +#define VAL_TPI_INFO_FSEL_VSI 0x06 /* TPI Info Byte #0, default value: 0x00 */ #define REG_TPI_INFO_B0 0x06c0
MHL3 protocol requires registry adjustments depending on chosen video mode. Necessary information is gathered in mode_fixup callback. In case of HDMI video modes driver should also send special AVI and MHL infoframes. Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> --- drivers/gpu/drm/bridge/sil-sii8620.c | 249 +++++++++++++++++++++++++++++++---- drivers/gpu/drm/bridge/sil-sii8620.h | 15 ++- 2 files changed, 231 insertions(+), 33 deletions(-)