1f5e7d251SAlgea Cao /* 2f5e7d251SAlgea Cao * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd 3f5e7d251SAlgea Cao * 4f5e7d251SAlgea Cao * SPDX-License-Identifier: GPL-2.0+ 5f5e7d251SAlgea Cao */ 6f5e7d251SAlgea Cao 7f5e7d251SAlgea Cao #include <common.h> 8f5e7d251SAlgea Cao #include <malloc.h> 9f5e7d251SAlgea Cao #include <syscon.h> 10f5e7d251SAlgea Cao #include <asm/arch-rockchip/clock.h> 118e2bab3fSAlgea Cao #include <asm/arch/vendor.h> 12f5e7d251SAlgea Cao #include <edid.h> 13e2bce6e4SKever Yang #include <dm/device.h> 141f71919fSShunqing Chen #include <dm/of_access.h> 158e2bab3fSAlgea Cao #include <dm/ofnode.h> 16e2bce6e4SKever Yang #include <dm/read.h> 17f5e7d251SAlgea Cao #include <linux/hdmi.h> 18f5e7d251SAlgea Cao #include <linux/media-bus-format.h> 19f5e7d251SAlgea Cao #include <linux/dw_hdmi.h> 20f5e7d251SAlgea Cao #include <asm/io.h> 21f5e7d251SAlgea Cao #include "rockchip_display.h" 22f5e7d251SAlgea Cao #include "rockchip_crtc.h" 23f5e7d251SAlgea Cao #include "rockchip_connector.h" 24f5e7d251SAlgea Cao #include "dw_hdmi.h" 258e2bab3fSAlgea Cao #include "rockchip_phy.h" 26f5e7d251SAlgea Cao 278e2bab3fSAlgea Cao #define HDCP_PRIVATE_KEY_SIZE 280 288e2bab3fSAlgea Cao #define HDCP_KEY_SHA_SIZE 20 298e2bab3fSAlgea Cao #define HDMI_HDCP1X_ID 5 30f5e7d251SAlgea Cao /* 31f5e7d251SAlgea Cao * Unless otherwise noted, entries in this table are 100% optimization. 32f5e7d251SAlgea Cao * Values can be obtained from hdmi_compute_n() but that function is 33f5e7d251SAlgea Cao * slow so we pre-compute values we expect to see. 34f5e7d251SAlgea Cao * 35f5e7d251SAlgea Cao * All 32k and 48k values are expected to be the same (due to the way 36f5e7d251SAlgea Cao * the math works) for any rate that's an exact kHz. 37f5e7d251SAlgea Cao */ 38f5e7d251SAlgea Cao static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { 39f5e7d251SAlgea Cao { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, }, 40f5e7d251SAlgea Cao { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, }, 41f5e7d251SAlgea Cao { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 42f5e7d251SAlgea Cao { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, 43f5e7d251SAlgea Cao { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, 44f5e7d251SAlgea Cao { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 45f5e7d251SAlgea Cao { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 46f5e7d251SAlgea Cao { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 47f5e7d251SAlgea Cao { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 48f5e7d251SAlgea Cao { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 49f5e7d251SAlgea Cao { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 50f5e7d251SAlgea Cao { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 51f5e7d251SAlgea Cao { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 52f5e7d251SAlgea Cao { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 53f5e7d251SAlgea Cao { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, 54f5e7d251SAlgea Cao { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 55f5e7d251SAlgea Cao { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, 56f5e7d251SAlgea Cao { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 57f5e7d251SAlgea Cao { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 58f5e7d251SAlgea Cao { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, 59f5e7d251SAlgea Cao { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 60f5e7d251SAlgea Cao { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 61f5e7d251SAlgea Cao { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 62f5e7d251SAlgea Cao { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 63f5e7d251SAlgea Cao { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 64f5e7d251SAlgea Cao { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 65f5e7d251SAlgea Cao { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 66f5e7d251SAlgea Cao { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 67f5e7d251SAlgea Cao { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 68f5e7d251SAlgea Cao { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 69f5e7d251SAlgea Cao { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, 70f5e7d251SAlgea Cao { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 71f5e7d251SAlgea Cao { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 72f5e7d251SAlgea Cao { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 73f5e7d251SAlgea Cao { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 74f5e7d251SAlgea Cao { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 75f5e7d251SAlgea Cao { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 76f5e7d251SAlgea Cao 77f5e7d251SAlgea Cao /* For 297 MHz+ HDMI spec have some other rule for setting N */ 78f5e7d251SAlgea Cao { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, 79f5e7d251SAlgea Cao { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, 80f5e7d251SAlgea Cao 81f5e7d251SAlgea Cao /* End of table */ 82f5e7d251SAlgea Cao { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, 83f5e7d251SAlgea Cao }; 84f5e7d251SAlgea Cao 85f5e7d251SAlgea Cao static const u16 csc_coeff_default[3][4] = { 86f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x0000, 0x0000 }, 87f5e7d251SAlgea Cao { 0x0000, 0x2000, 0x0000, 0x0000 }, 88f5e7d251SAlgea Cao { 0x0000, 0x0000, 0x2000, 0x0000 } 89f5e7d251SAlgea Cao }; 90f5e7d251SAlgea Cao 91f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu601[3][4] = { 92f5e7d251SAlgea Cao { 0x2000, 0x6926, 0x74fd, 0x010e }, 93f5e7d251SAlgea Cao { 0x2000, 0x2cdd, 0x0000, 0x7e9a }, 94f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x38b4, 0x7e3b } 95f5e7d251SAlgea Cao }; 96f5e7d251SAlgea Cao 97f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu709[3][4] = { 98f5e7d251SAlgea Cao { 0x2000, 0x7106, 0x7a02, 0x00a7 }, 99f5e7d251SAlgea Cao { 0x2000, 0x3264, 0x0000, 0x7e6d }, 100f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x3b61, 0x7e25 } 101f5e7d251SAlgea Cao }; 102f5e7d251SAlgea Cao 103f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu601[3][4] = { 104f5e7d251SAlgea Cao { 0x2591, 0x1322, 0x074b, 0x0000 }, 105f5e7d251SAlgea Cao { 0x6535, 0x2000, 0x7acc, 0x0200 }, 106f5e7d251SAlgea Cao { 0x6acd, 0x7534, 0x2000, 0x0200 } 107f5e7d251SAlgea Cao }; 108f5e7d251SAlgea Cao 109f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu709[3][4] = { 110f5e7d251SAlgea Cao { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, 111f5e7d251SAlgea Cao { 0x62f0, 0x2000, 0x7d11, 0x0200 }, 112f5e7d251SAlgea Cao { 0x6756, 0x78ab, 0x2000, 0x0200 } 113f5e7d251SAlgea Cao }; 114f5e7d251SAlgea Cao 115b5016cf2SAlgea Cao static const u16 csc_coeff_full_to_limited[3][4] = { 116b5016cf2SAlgea Cao { 0x36f7, 0x0000, 0x0000, 0x0040 }, 117b5016cf2SAlgea Cao { 0x0000, 0x36f7, 0x0000, 0x0040 }, 118b5016cf2SAlgea Cao { 0x0000, 0x0000, 0x36f7, 0x0040 } 119b5016cf2SAlgea Cao }; 120b5016cf2SAlgea Cao 121f5e7d251SAlgea Cao struct hdmi_vmode { 122f5e7d251SAlgea Cao bool mdataenablepolarity; 123f5e7d251SAlgea Cao 124f5e7d251SAlgea Cao unsigned int mpixelclock; 125f5e7d251SAlgea Cao unsigned int mpixelrepetitioninput; 126f5e7d251SAlgea Cao unsigned int mpixelrepetitionoutput; 1278e2bab3fSAlgea Cao unsigned int mtmdsclock; 128f5e7d251SAlgea Cao }; 129f5e7d251SAlgea Cao 130f5e7d251SAlgea Cao struct hdmi_data_info { 131f5e7d251SAlgea Cao unsigned int enc_in_bus_format; 132f5e7d251SAlgea Cao unsigned int enc_out_bus_format; 133f5e7d251SAlgea Cao unsigned int enc_in_encoding; 134f5e7d251SAlgea Cao unsigned int enc_out_encoding; 135b5016cf2SAlgea Cao unsigned int quant_range; 136f5e7d251SAlgea Cao unsigned int pix_repet_factor; 137f5e7d251SAlgea Cao struct hdmi_vmode video_mode; 138f5e7d251SAlgea Cao }; 139f5e7d251SAlgea Cao 140f5e7d251SAlgea Cao struct dw_hdmi_phy_data { 141f5e7d251SAlgea Cao enum dw_hdmi_phy_type type; 142f5e7d251SAlgea Cao const char *name; 143f5e7d251SAlgea Cao unsigned int gen; 144f5e7d251SAlgea Cao bool has_svsret; 145f5e7d251SAlgea Cao int (*configure)(struct dw_hdmi *hdmi, 146f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata, 147f5e7d251SAlgea Cao unsigned long mpixelclock); 148f5e7d251SAlgea Cao }; 149f5e7d251SAlgea Cao 1508e2bab3fSAlgea Cao struct hdcp_keys { 1518e2bab3fSAlgea Cao u8 KSV[8]; 1528e2bab3fSAlgea Cao u8 devicekey[HDCP_PRIVATE_KEY_SIZE]; 1538e2bab3fSAlgea Cao u8 sha1[HDCP_KEY_SHA_SIZE]; 1548e2bab3fSAlgea Cao u8 seeds[2]; 1558e2bab3fSAlgea Cao }; 1568e2bab3fSAlgea Cao 1578e2bab3fSAlgea Cao struct dw_hdmi_i2c { 1588e2bab3fSAlgea Cao u8 slave_reg; 1598e2bab3fSAlgea Cao bool is_regaddr; 1608e2bab3fSAlgea Cao bool is_segment; 1618e2bab3fSAlgea Cao 1628e2bab3fSAlgea Cao unsigned int scl_high_ns; 1638e2bab3fSAlgea Cao unsigned int scl_low_ns; 1648e2bab3fSAlgea Cao }; 1658e2bab3fSAlgea Cao 166f5e7d251SAlgea Cao struct dw_hdmi { 167*cb17ca6cSSandy Huang int id; 168f5e7d251SAlgea Cao enum dw_hdmi_devtype dev_type; 169f5e7d251SAlgea Cao unsigned int version; 170f5e7d251SAlgea Cao struct hdmi_data_info hdmi_data; 171f5e7d251SAlgea Cao struct hdmi_edid_data edid_data; 172f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data; 1738e2bab3fSAlgea Cao struct ddc_adapter adap; 174f5e7d251SAlgea Cao 175f5e7d251SAlgea Cao int vic; 176f5e7d251SAlgea Cao int io_width; 177f5e7d251SAlgea Cao 178f5e7d251SAlgea Cao unsigned long bus_format; 179f5e7d251SAlgea Cao bool cable_plugin; 180f5e7d251SAlgea Cao bool sink_is_hdmi; 181f5e7d251SAlgea Cao bool sink_has_audio; 182f5e7d251SAlgea Cao void *regs; 183f5e7d251SAlgea Cao void *grf; 1848e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c; 185f5e7d251SAlgea Cao 186f5e7d251SAlgea Cao struct { 187f5e7d251SAlgea Cao const struct dw_hdmi_phy_ops *ops; 188f5e7d251SAlgea Cao const char *name; 189f5e7d251SAlgea Cao void *data; 190f5e7d251SAlgea Cao bool enabled; 191f5e7d251SAlgea Cao } phy; 192f5e7d251SAlgea Cao 193f5e7d251SAlgea Cao struct drm_display_mode previous_mode; 194f5e7d251SAlgea Cao 195f5e7d251SAlgea Cao unsigned int sample_rate; 196f5e7d251SAlgea Cao unsigned int audio_cts; 197f5e7d251SAlgea Cao unsigned int audio_n; 198f5e7d251SAlgea Cao bool audio_enable; 1998e2bab3fSAlgea Cao bool scramble_low_rates; 200f5e7d251SAlgea Cao 201f5e7d251SAlgea Cao void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); 202f5e7d251SAlgea Cao u8 (*read)(struct dw_hdmi *hdmi, int offset); 2038e2bab3fSAlgea Cao 2048e2bab3fSAlgea Cao bool hdcp1x_enable; 205f5e7d251SAlgea Cao }; 206f5e7d251SAlgea Cao 207f5e7d251SAlgea Cao static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset) 208f5e7d251SAlgea Cao { 209f5e7d251SAlgea Cao writel(val, hdmi->regs + (offset << 2)); 210f5e7d251SAlgea Cao } 211f5e7d251SAlgea Cao 212f5e7d251SAlgea Cao static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset) 213f5e7d251SAlgea Cao { 214f5e7d251SAlgea Cao return readl(hdmi->regs + (offset << 2)); 215f5e7d251SAlgea Cao } 216f5e7d251SAlgea Cao 217f5e7d251SAlgea Cao static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) 218f5e7d251SAlgea Cao { 219f5e7d251SAlgea Cao writeb(val, hdmi->regs + offset); 220f5e7d251SAlgea Cao } 221f5e7d251SAlgea Cao 222f5e7d251SAlgea Cao static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset) 223f5e7d251SAlgea Cao { 224f5e7d251SAlgea Cao return readb(hdmi->regs + offset); 225f5e7d251SAlgea Cao } 226f5e7d251SAlgea Cao 227f5e7d251SAlgea Cao static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) 228f5e7d251SAlgea Cao { 229f5e7d251SAlgea Cao hdmi->write(hdmi, val, offset); 230f5e7d251SAlgea Cao } 231f5e7d251SAlgea Cao 232f5e7d251SAlgea Cao static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) 233f5e7d251SAlgea Cao { 234f5e7d251SAlgea Cao return hdmi->read(hdmi, offset); 235f5e7d251SAlgea Cao } 236f5e7d251SAlgea Cao 237f5e7d251SAlgea Cao static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) 238f5e7d251SAlgea Cao { 239f5e7d251SAlgea Cao u8 val = hdmi_readb(hdmi, reg) & ~mask; 240f5e7d251SAlgea Cao 241f5e7d251SAlgea Cao val |= data & mask; 242f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, reg); 243f5e7d251SAlgea Cao } 244f5e7d251SAlgea Cao 245f5e7d251SAlgea Cao static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, 246f5e7d251SAlgea Cao u8 shift, u8 mask) 247f5e7d251SAlgea Cao { 248f5e7d251SAlgea Cao hdmi_modb(hdmi, data << shift, mask, reg); 249f5e7d251SAlgea Cao } 250f5e7d251SAlgea Cao 251f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) 252f5e7d251SAlgea Cao { 253f5e7d251SAlgea Cao switch (bus_format) { 254f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 255f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 256f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 257f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 258f5e7d251SAlgea Cao return true; 259f5e7d251SAlgea Cao 260f5e7d251SAlgea Cao default: 261f5e7d251SAlgea Cao return false; 262f5e7d251SAlgea Cao } 263f5e7d251SAlgea Cao } 264f5e7d251SAlgea Cao 265f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) 266f5e7d251SAlgea Cao { 267f5e7d251SAlgea Cao switch (bus_format) { 268f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 269f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 270f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 271f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 272f5e7d251SAlgea Cao return true; 273f5e7d251SAlgea Cao 274f5e7d251SAlgea Cao default: 275f5e7d251SAlgea Cao return false; 276f5e7d251SAlgea Cao } 277f5e7d251SAlgea Cao } 278f5e7d251SAlgea Cao 279f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) 280f5e7d251SAlgea Cao { 281f5e7d251SAlgea Cao switch (bus_format) { 282f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 283f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 284f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 285f5e7d251SAlgea Cao return true; 286f5e7d251SAlgea Cao 287f5e7d251SAlgea Cao default: 288f5e7d251SAlgea Cao return false; 289f5e7d251SAlgea Cao } 290f5e7d251SAlgea Cao } 291f5e7d251SAlgea Cao 292f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) 293f5e7d251SAlgea Cao { 294f5e7d251SAlgea Cao switch (bus_format) { 295f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 296f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 297f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 298f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 299f5e7d251SAlgea Cao return true; 300f5e7d251SAlgea Cao 301f5e7d251SAlgea Cao default: 302f5e7d251SAlgea Cao return false; 303f5e7d251SAlgea Cao } 304f5e7d251SAlgea Cao } 305f5e7d251SAlgea Cao 306f5e7d251SAlgea Cao static int hdmi_bus_fmt_color_depth(unsigned int bus_format) 307f5e7d251SAlgea Cao { 308f5e7d251SAlgea Cao switch (bus_format) { 309f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 310f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 311f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 312f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 313f5e7d251SAlgea Cao return 8; 314f5e7d251SAlgea Cao 315f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 316f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 317f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 318f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 319f5e7d251SAlgea Cao return 10; 320f5e7d251SAlgea Cao 321f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 322f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 323f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 324f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 325f5e7d251SAlgea Cao return 12; 326f5e7d251SAlgea Cao 327f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 328f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 329f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 330f5e7d251SAlgea Cao return 16; 331f5e7d251SAlgea Cao 332f5e7d251SAlgea Cao default: 333f5e7d251SAlgea Cao return 0; 334f5e7d251SAlgea Cao } 335f5e7d251SAlgea Cao } 336f5e7d251SAlgea Cao 337f5e7d251SAlgea Cao static int is_color_space_conversion(struct dw_hdmi *hdmi) 338f5e7d251SAlgea Cao { 339b5016cf2SAlgea Cao struct drm_display_mode *mode = 340b5016cf2SAlgea Cao hdmi->edid_data.preferred_mode; 341b5016cf2SAlgea Cao bool is_cea_default; 342b5016cf2SAlgea Cao 343b5016cf2SAlgea Cao is_cea_default = (drm_match_cea_mode(mode) > 1) && 344b5016cf2SAlgea Cao (hdmi->hdmi_data.quant_range == 345b5016cf2SAlgea Cao HDMI_QUANTIZATION_RANGE_DEFAULT); 346b5016cf2SAlgea Cao 347b5016cf2SAlgea Cao /* 348b5016cf2SAlgea Cao * When output is rgb limited range or default range with 349b5016cf2SAlgea Cao * cea mode, csc should be enabled. 350b5016cf2SAlgea Cao */ 351b5016cf2SAlgea Cao if (hdmi->hdmi_data.enc_in_bus_format != 352b5016cf2SAlgea Cao hdmi->hdmi_data.enc_out_bus_format || 353b5016cf2SAlgea Cao ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED || 354b5016cf2SAlgea Cao is_cea_default) && 355b5016cf2SAlgea Cao hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format))) 356b5016cf2SAlgea Cao return 1; 357b5016cf2SAlgea Cao 358b5016cf2SAlgea Cao return 0; 359f5e7d251SAlgea Cao } 360f5e7d251SAlgea Cao 361f5e7d251SAlgea Cao static int is_color_space_decimation(struct dw_hdmi *hdmi) 362f5e7d251SAlgea Cao { 363f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 364f5e7d251SAlgea Cao return 0; 365f5e7d251SAlgea Cao 366f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) || 367f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format)) 368f5e7d251SAlgea Cao return 1; 369f5e7d251SAlgea Cao 370f5e7d251SAlgea Cao return 0; 371f5e7d251SAlgea Cao } 372f5e7d251SAlgea Cao 373f5e7d251SAlgea Cao static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, 374f5e7d251SAlgea Cao unsigned char bit) 375f5e7d251SAlgea Cao { 376f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, 377f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); 378f5e7d251SAlgea Cao } 379f5e7d251SAlgea Cao 380f5e7d251SAlgea Cao static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, 381f5e7d251SAlgea Cao unsigned char bit) 382f5e7d251SAlgea Cao { 383f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, 384f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); 385f5e7d251SAlgea Cao } 386f5e7d251SAlgea Cao 387f5e7d251SAlgea Cao static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, 388f5e7d251SAlgea Cao unsigned char bit) 389f5e7d251SAlgea Cao { 390f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, 391f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); 392f5e7d251SAlgea Cao } 393f5e7d251SAlgea Cao 394f5e7d251SAlgea Cao static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, 395f5e7d251SAlgea Cao unsigned char bit) 396f5e7d251SAlgea Cao { 397f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); 398f5e7d251SAlgea Cao } 399f5e7d251SAlgea Cao 400f5e7d251SAlgea Cao static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, 401f5e7d251SAlgea Cao unsigned char bit) 402f5e7d251SAlgea Cao { 403f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); 404f5e7d251SAlgea Cao } 405f5e7d251SAlgea Cao 4068e2bab3fSAlgea Cao static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, 4078e2bab3fSAlgea Cao unsigned char *buf, unsigned int length) 4088e2bab3fSAlgea Cao { 4098e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 4108e2bab3fSAlgea Cao int interrupt = 0, i = 20; 4118e2bab3fSAlgea Cao 4128e2bab3fSAlgea Cao if (!i2c->is_regaddr) { 4138e2bab3fSAlgea Cao printf("set read register address to 0\n"); 4148e2bab3fSAlgea Cao i2c->slave_reg = 0x00; 4158e2bab3fSAlgea Cao i2c->is_regaddr = true; 4168e2bab3fSAlgea Cao } 4178e2bab3fSAlgea Cao 4188e2bab3fSAlgea Cao while (length--) { 4198e2bab3fSAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); 4208e2bab3fSAlgea Cao if (i2c->is_segment) 4218e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, 4228e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 4238e2bab3fSAlgea Cao else 4248e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, 4258e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 4268e2bab3fSAlgea Cao 4278e2bab3fSAlgea Cao while (i--) { 4288e2bab3fSAlgea Cao udelay(1000); 4298e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0); 4308e2bab3fSAlgea Cao if (interrupt) 4318e2bab3fSAlgea Cao hdmi_writeb(hdmi, interrupt, 4328e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0); 4338e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE | 4348e2bab3fSAlgea Cao m_I2CM_ERROR)) 4358e2bab3fSAlgea Cao break; 4368e2bab3fSAlgea Cao } 4378e2bab3fSAlgea Cao 4388e2bab3fSAlgea Cao if (!interrupt) { 4398e2bab3fSAlgea Cao printf("[%s] i2c read reg[0x%02x] no interrupt\n", 4408e2bab3fSAlgea Cao __func__, i2c->slave_reg); 4418e2bab3fSAlgea Cao return -EAGAIN; 4428e2bab3fSAlgea Cao } 4438e2bab3fSAlgea Cao 4448e2bab3fSAlgea Cao /* Check for error condition on the bus */ 4458e2bab3fSAlgea Cao if (interrupt & HDMI_IH_I2CM_STAT0_ERROR) { 4468e2bab3fSAlgea Cao printf("[%s] read reg[0x%02x] data error:0x%02x\n", 4478e2bab3fSAlgea Cao __func__, i2c->slave_reg, interrupt); 4488e2bab3fSAlgea Cao return -EIO; 4498e2bab3fSAlgea Cao } 4508e2bab3fSAlgea Cao 4518e2bab3fSAlgea Cao i = 20; 4528e2bab3fSAlgea Cao *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); 4538e2bab3fSAlgea Cao } 4548e2bab3fSAlgea Cao i2c->is_segment = false; 4558e2bab3fSAlgea Cao 4568e2bab3fSAlgea Cao return 0; 4578e2bab3fSAlgea Cao } 4588e2bab3fSAlgea Cao 4598e2bab3fSAlgea Cao static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi, 4608e2bab3fSAlgea Cao unsigned char *buf, unsigned int length) 4618e2bab3fSAlgea Cao { 4628e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 4638e2bab3fSAlgea Cao int i = 20; 4648e2bab3fSAlgea Cao u8 interrupt = 0; 4658e2bab3fSAlgea Cao 4668e2bab3fSAlgea Cao if (!i2c->is_regaddr) { 4678e2bab3fSAlgea Cao /* Use the first write byte as register address */ 4688e2bab3fSAlgea Cao i2c->slave_reg = buf[0]; 4698e2bab3fSAlgea Cao length--; 4708e2bab3fSAlgea Cao buf++; 4718e2bab3fSAlgea Cao i2c->is_regaddr = true; 4728e2bab3fSAlgea Cao } 4738e2bab3fSAlgea Cao 4748e2bab3fSAlgea Cao while (length--) { 4758e2bab3fSAlgea Cao hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO); 4768e2bab3fSAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); 4778e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE, 4788e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 4798e2bab3fSAlgea Cao 4808e2bab3fSAlgea Cao while (i--) { 4818e2bab3fSAlgea Cao udelay(1000); 4828e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0); 4838e2bab3fSAlgea Cao if (interrupt) 4848e2bab3fSAlgea Cao hdmi_writeb(hdmi, 4858e2bab3fSAlgea Cao interrupt, HDMI_IH_I2CM_STAT0); 4868e2bab3fSAlgea Cao 4878e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ | 4888e2bab3fSAlgea Cao m_I2CM_DONE | m_I2CM_ERROR)) 4898e2bab3fSAlgea Cao break; 4908e2bab3fSAlgea Cao } 4918e2bab3fSAlgea Cao 4928e2bab3fSAlgea Cao if ((interrupt & m_I2CM_ERROR) || (i == -1)) { 4938e2bab3fSAlgea Cao printf("[%s] write data error\n", __func__); 4948e2bab3fSAlgea Cao return -EIO; 4958e2bab3fSAlgea Cao } else if (interrupt & m_I2CM_DONE) { 4968e2bab3fSAlgea Cao printf("[%s] write offset %02x success\n", 4978e2bab3fSAlgea Cao __func__, i2c->slave_reg); 4988e2bab3fSAlgea Cao return -EAGAIN; 4998e2bab3fSAlgea Cao } 5008e2bab3fSAlgea Cao 5018e2bab3fSAlgea Cao i = 20; 5028e2bab3fSAlgea Cao } 5038e2bab3fSAlgea Cao 5048e2bab3fSAlgea Cao return 0; 5058e2bab3fSAlgea Cao } 5068e2bab3fSAlgea Cao 5078e2bab3fSAlgea Cao static int dw_hdmi_i2c_xfer(struct ddc_adapter *adap, 5088e2bab3fSAlgea Cao struct i2c_msg *msgs, int num) 5098e2bab3fSAlgea Cao { 5108e2bab3fSAlgea Cao struct dw_hdmi *hdmi = container_of(adap, struct dw_hdmi, adap); 5118e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 5128e2bab3fSAlgea Cao u8 addr = msgs[0].addr; 5138e2bab3fSAlgea Cao int i, ret = 0; 5148e2bab3fSAlgea Cao 5158e2bab3fSAlgea Cao printf("xfer: num: %d, addr: %#x\n", num, addr); 5168e2bab3fSAlgea Cao for (i = 0; i < num; i++) { 5178e2bab3fSAlgea Cao if (msgs[i].len == 0) { 5188e2bab3fSAlgea Cao printf("unsupported transfer %d/%d, no data\n", 5198e2bab3fSAlgea Cao i + 1, num); 5208e2bab3fSAlgea Cao return -EOPNOTSUPP; 5218e2bab3fSAlgea Cao } 5228e2bab3fSAlgea Cao } 5238e2bab3fSAlgea Cao 5248e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0); 5258e2bab3fSAlgea Cao 5268e2bab3fSAlgea Cao /* Set slave device address taken from the first I2C message */ 5278e2bab3fSAlgea Cao if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) 5288e2bab3fSAlgea Cao addr = DDC_ADDR; 5298e2bab3fSAlgea Cao hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE); 5308e2bab3fSAlgea Cao 5318e2bab3fSAlgea Cao /* Set slave device register address on transfer */ 5328e2bab3fSAlgea Cao i2c->is_regaddr = false; 5338e2bab3fSAlgea Cao 5348e2bab3fSAlgea Cao /* Set segment pointer for I2C extended read mode operation */ 5358e2bab3fSAlgea Cao i2c->is_segment = false; 5368e2bab3fSAlgea Cao 5378e2bab3fSAlgea Cao for (i = 0; i < num; i++) { 5388e2bab3fSAlgea Cao debug("xfer: num: %d/%d, len: %d, flags: %#x\n", 5398e2bab3fSAlgea Cao i + 1, num, msgs[i].len, msgs[i].flags); 5408e2bab3fSAlgea Cao if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { 5418e2bab3fSAlgea Cao i2c->is_segment = true; 5428e2bab3fSAlgea Cao hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR); 5438e2bab3fSAlgea Cao hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR); 5448e2bab3fSAlgea Cao } else { 5458e2bab3fSAlgea Cao if (msgs[i].flags & I2C_M_RD) 5468e2bab3fSAlgea Cao ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, 5478e2bab3fSAlgea Cao msgs[i].len); 5488e2bab3fSAlgea Cao else 5498e2bab3fSAlgea Cao ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, 5508e2bab3fSAlgea Cao msgs[i].len); 5518e2bab3fSAlgea Cao } 5528e2bab3fSAlgea Cao if (ret < 0) 5538e2bab3fSAlgea Cao break; 5548e2bab3fSAlgea Cao } 5558e2bab3fSAlgea Cao 5568e2bab3fSAlgea Cao if (!ret) 5578e2bab3fSAlgea Cao ret = num; 5588e2bab3fSAlgea Cao 5598e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */ 5608e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 5618e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0); 5628e2bab3fSAlgea Cao 5638e2bab3fSAlgea Cao return ret; 5648e2bab3fSAlgea Cao } 5658e2bab3fSAlgea Cao 566f5e7d251SAlgea Cao static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) 567f5e7d251SAlgea Cao { 568f5e7d251SAlgea Cao u32 val; 569f5e7d251SAlgea Cao 570f5e7d251SAlgea Cao while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { 571f5e7d251SAlgea Cao if (msec-- == 0) 572f5e7d251SAlgea Cao return false; 573f5e7d251SAlgea Cao udelay(1000); 574f5e7d251SAlgea Cao } 575f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0); 576f5e7d251SAlgea Cao 577f5e7d251SAlgea Cao return true; 578f5e7d251SAlgea Cao } 579f5e7d251SAlgea Cao 580f5e7d251SAlgea Cao static void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, 581f5e7d251SAlgea Cao unsigned char addr) 582f5e7d251SAlgea Cao { 583f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); 584f5e7d251SAlgea Cao hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); 585f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 8), 586f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_1_ADDR); 587f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 0), 588f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_0_ADDR); 589f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, 590f5e7d251SAlgea Cao HDMI_PHY_I2CM_OPERATION_ADDR); 591f5e7d251SAlgea Cao hdmi_phy_wait_i2c_done(hdmi, 1000); 592f5e7d251SAlgea Cao } 593f5e7d251SAlgea Cao 594f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) 595f5e7d251SAlgea Cao { 596f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, 597f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_OFFSET, 598f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_MASK); 599f5e7d251SAlgea Cao } 600f5e7d251SAlgea Cao 601f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) 602f5e7d251SAlgea Cao { 603f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 604f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_OFFSET, 605f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_MASK); 606f5e7d251SAlgea Cao } 607f5e7d251SAlgea Cao 608f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable) 609f5e7d251SAlgea Cao { 610f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 611f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_OFFSET, 612f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_MASK); 613f5e7d251SAlgea Cao } 614f5e7d251SAlgea Cao 615f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) 616f5e7d251SAlgea Cao { 617f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 618f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, 619f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_MASK); 620f5e7d251SAlgea Cao } 621f5e7d251SAlgea Cao 622f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable) 623f5e7d251SAlgea Cao { 624f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 625f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, 626f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); 627f5e7d251SAlgea Cao } 628f5e7d251SAlgea Cao 629f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable) 630f5e7d251SAlgea Cao { 631f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 632f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, 633f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_MASK); 634f5e7d251SAlgea Cao } 635f5e7d251SAlgea Cao 636f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) 637f5e7d251SAlgea Cao { 638f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 639f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_OFFSET, 640f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_MASK); 641f5e7d251SAlgea Cao } 642f5e7d251SAlgea Cao 643f5e7d251SAlgea Cao static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) 644f5e7d251SAlgea Cao { 645f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 646f5e7d251SAlgea Cao unsigned int i; 647f5e7d251SAlgea Cao u16 val; 648f5e7d251SAlgea Cao 649f5e7d251SAlgea Cao if (phy->gen == 1) { 650f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0); 651f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, true); 652f5e7d251SAlgea Cao return; 653f5e7d251SAlgea Cao } 654f5e7d251SAlgea Cao 655f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 0); 656f5e7d251SAlgea Cao 657f5e7d251SAlgea Cao /* 658f5e7d251SAlgea Cao * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went 659f5e7d251SAlgea Cao * to low power mode. 660f5e7d251SAlgea Cao */ 661f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) { 662f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0); 663f5e7d251SAlgea Cao if (!(val & HDMI_PHY_TX_PHY_LOCK)) 664f5e7d251SAlgea Cao break; 665f5e7d251SAlgea Cao 666f5e7d251SAlgea Cao udelay(2000); 667f5e7d251SAlgea Cao } 668f5e7d251SAlgea Cao 669f5e7d251SAlgea Cao if (val & HDMI_PHY_TX_PHY_LOCK) 670f5e7d251SAlgea Cao printf("PHY failed to power down\n"); 671f5e7d251SAlgea Cao else 672f5e7d251SAlgea Cao printf("PHY powered down in %u iterations\n", i); 673f5e7d251SAlgea Cao 674f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 1); 675f5e7d251SAlgea Cao } 676f5e7d251SAlgea Cao 677f5e7d251SAlgea Cao static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) 678f5e7d251SAlgea Cao { 679f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 680f5e7d251SAlgea Cao unsigned int i; 681f5e7d251SAlgea Cao u8 val; 682f5e7d251SAlgea Cao 683f5e7d251SAlgea Cao if (phy->gen == 1) { 684f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, false); 685f5e7d251SAlgea Cao 686f5e7d251SAlgea Cao /* Toggle TMDS enable. */ 687f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0); 688f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 1); 689f5e7d251SAlgea Cao return 0; 690f5e7d251SAlgea Cao } 691f5e7d251SAlgea Cao 692f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 1); 693f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 0); 694f5e7d251SAlgea Cao 695f5e7d251SAlgea Cao /* Wait for PHY PLL lock */ 696f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) { 697f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; 698f5e7d251SAlgea Cao if (val) 699f5e7d251SAlgea Cao break; 700f5e7d251SAlgea Cao 701f5e7d251SAlgea Cao udelay(2000); 702f5e7d251SAlgea Cao } 703f5e7d251SAlgea Cao 704f5e7d251SAlgea Cao if (!val) { 705f5e7d251SAlgea Cao printf("PHY PLL failed to lock\n"); 706f5e7d251SAlgea Cao return -ETIMEDOUT; 707f5e7d251SAlgea Cao } 708f5e7d251SAlgea Cao printf("PHY PLL locked %u iterations\n", i); 7098e2bab3fSAlgea Cao 710f5e7d251SAlgea Cao return 0; 711f5e7d251SAlgea Cao } 712f5e7d251SAlgea Cao 713f5e7d251SAlgea Cao /* 714f5e7d251SAlgea Cao * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available 715f5e7d251SAlgea Cao * information the DWC MHL PHY has the same register layout and is thus also 716f5e7d251SAlgea Cao * supported by this function. 717f5e7d251SAlgea Cao */ 718f5e7d251SAlgea Cao static 719f5e7d251SAlgea Cao int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, 720f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata, 721f5e7d251SAlgea Cao unsigned long mpixelclock) 722f5e7d251SAlgea Cao { 723f5e7d251SAlgea Cao const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; 724f5e7d251SAlgea Cao const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; 725f5e7d251SAlgea Cao const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; 7268e2bab3fSAlgea Cao unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; 7278e2bab3fSAlgea Cao unsigned int depth = 7288e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 7298e2bab3fSAlgea Cao 7308e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) && 7318e2bab3fSAlgea Cao pdata->mpll_cfg_420) 7328e2bab3fSAlgea Cao mpll_config = pdata->mpll_cfg_420; 733f5e7d251SAlgea Cao 734f5e7d251SAlgea Cao /* PLL/MPLL Cfg - always match on final entry */ 735f5e7d251SAlgea Cao for (; mpll_config->mpixelclock != ~0UL; mpll_config++) 736f5e7d251SAlgea Cao if (mpixelclock <= mpll_config->mpixelclock) 737f5e7d251SAlgea Cao break; 738f5e7d251SAlgea Cao 739f5e7d251SAlgea Cao for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) 7408e2bab3fSAlgea Cao if (tmdsclock <= curr_ctrl->mpixelclock) 741f5e7d251SAlgea Cao break; 742f5e7d251SAlgea Cao 743f5e7d251SAlgea Cao for (; phy_config->mpixelclock != ~0UL; phy_config++) 7448e2bab3fSAlgea Cao if (tmdsclock <= phy_config->mpixelclock) 745f5e7d251SAlgea Cao break; 746f5e7d251SAlgea Cao 747f5e7d251SAlgea Cao if (mpll_config->mpixelclock == ~0UL || 748f5e7d251SAlgea Cao curr_ctrl->mpixelclock == ~0UL || 749f5e7d251SAlgea Cao phy_config->mpixelclock == ~0UL) 750f5e7d251SAlgea Cao return -EINVAL; 751f5e7d251SAlgea Cao 7528e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 7538e2bab3fSAlgea Cao depth = fls(depth - 8); 754f5e7d251SAlgea Cao else 7558e2bab3fSAlgea Cao depth = 0; 7568e2bab3fSAlgea Cao if (depth) 7578e2bab3fSAlgea Cao depth--; 7588e2bab3fSAlgea Cao 7598e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce, 760f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CPCE_CTRL); 7618e2bab3fSAlgea Cao 7628e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp, 763f5e7d251SAlgea Cao HDMI_3D_TX_PHY_GMPCTRL); 7648e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth], 765f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CURRCTRL); 766f5e7d251SAlgea Cao 767f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); 768f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, 769f5e7d251SAlgea Cao HDMI_3D_TX_PHY_MSM_CTRL); 770f5e7d251SAlgea Cao 7718e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); 7728e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 773f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CKSYMTXCTRL); 7748e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 775f5e7d251SAlgea Cao HDMI_3D_TX_PHY_VLEVCTRL); 776f5e7d251SAlgea Cao 777f5e7d251SAlgea Cao return 0; 778f5e7d251SAlgea Cao } 779f5e7d251SAlgea Cao 780f5e7d251SAlgea Cao static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { 781f5e7d251SAlgea Cao { 782f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, 783f5e7d251SAlgea Cao .name = "DWC HDMI TX PHY", 784f5e7d251SAlgea Cao .gen = 1, 785f5e7d251SAlgea Cao }, { 786f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC, 787f5e7d251SAlgea Cao .name = "DWC MHL PHY + HEAC PHY", 788f5e7d251SAlgea Cao .gen = 2, 789f5e7d251SAlgea Cao .has_svsret = true, 790f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 791f5e7d251SAlgea Cao }, { 792f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY, 793f5e7d251SAlgea Cao .name = "DWC MHL PHY", 794f5e7d251SAlgea Cao .gen = 2, 795f5e7d251SAlgea Cao .has_svsret = true, 796f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 797f5e7d251SAlgea Cao }, { 798f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, 799f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY + HEAC PHY", 800f5e7d251SAlgea Cao .gen = 2, 801f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 802f5e7d251SAlgea Cao }, { 803f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, 804f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY", 805f5e7d251SAlgea Cao .gen = 2, 806f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 807f5e7d251SAlgea Cao }, { 808f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, 809f5e7d251SAlgea Cao .name = "DWC HDMI 2.0 TX PHY", 810f5e7d251SAlgea Cao .gen = 2, 811f5e7d251SAlgea Cao .has_svsret = true, 812f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 813f5e7d251SAlgea Cao }, { 814f5e7d251SAlgea Cao .type = DW_HDMI_PHY_VENDOR_PHY, 815f5e7d251SAlgea Cao .name = "Vendor PHY", 816f5e7d251SAlgea Cao } 817f5e7d251SAlgea Cao }; 818f5e7d251SAlgea Cao 819f5e7d251SAlgea Cao static int rockchip_dw_hdmi_scrambling_enable(struct dw_hdmi *hdmi, 820f5e7d251SAlgea Cao int enable) 821f5e7d251SAlgea Cao { 8228e2bab3fSAlgea Cao u8 stat; 823f5e7d251SAlgea Cao 8248e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat); 8258e2bab3fSAlgea Cao 826f5e7d251SAlgea Cao if (stat < 0) { 827f5e7d251SAlgea Cao debug("Failed to read tmds config\n"); 828f5e7d251SAlgea Cao return false; 829f5e7d251SAlgea Cao } 830f5e7d251SAlgea Cao 831f5e7d251SAlgea Cao if (enable == 1) { 832f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */ 833f5e7d251SAlgea Cao stat |= SCDC_SCRAMBLING_ENABLE; 8348e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 835f5e7d251SAlgea Cao /* TMDS software reset request */ 836f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, 837f5e7d251SAlgea Cao HDMI_MC_SWRSTZ); 838f5e7d251SAlgea Cao /* Enable/Disable Scrambling */ 839f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL); 840f5e7d251SAlgea Cao } else { 841f5e7d251SAlgea Cao /* Enable/Disable Scrambling */ 842f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL); 843f5e7d251SAlgea Cao /* TMDS software reset request */ 844f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, 845f5e7d251SAlgea Cao HDMI_MC_SWRSTZ); 846f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */ 847f5e7d251SAlgea Cao stat &= ~SCDC_SCRAMBLING_ENABLE; 8488e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 849f5e7d251SAlgea Cao } 850f5e7d251SAlgea Cao 851f5e7d251SAlgea Cao return 0; 852f5e7d251SAlgea Cao } 853f5e7d251SAlgea Cao 854f5e7d251SAlgea Cao static void rockchip_dw_hdmi_scdc_set_tmds_rate(struct dw_hdmi *hdmi) 855f5e7d251SAlgea Cao { 8568e2bab3fSAlgea Cao u8 stat; 857f5e7d251SAlgea Cao 8588e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat); 8598e2bab3fSAlgea Cao if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) 860f5e7d251SAlgea Cao stat |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 861f5e7d251SAlgea Cao else 862f5e7d251SAlgea Cao stat &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 8638e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 864f5e7d251SAlgea Cao } 865f5e7d251SAlgea Cao 866f5e7d251SAlgea Cao static int hdmi_phy_configure(struct dw_hdmi *hdmi) 867f5e7d251SAlgea Cao { 868f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 869f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; 870f5e7d251SAlgea Cao unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; 8718e2bab3fSAlgea Cao unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; 8728e2bab3fSAlgea Cao int ret; 873f5e7d251SAlgea Cao 874f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi); 875f5e7d251SAlgea Cao 876f5e7d251SAlgea Cao /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ 877f5e7d251SAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported) 878f5e7d251SAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi); 879f5e7d251SAlgea Cao 880f5e7d251SAlgea Cao /* Leave low power consumption mode by asserting SVSRET. */ 881f5e7d251SAlgea Cao if (phy->has_svsret) 882f5e7d251SAlgea Cao dw_hdmi_phy_enable_svsret(hdmi, 1); 883f5e7d251SAlgea Cao 884f5e7d251SAlgea Cao /* PHY reset. The reset signal is active high on Gen2 PHYs. */ 885f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); 886f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); 887f5e7d251SAlgea Cao 888f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); 889f5e7d251SAlgea Cao 890f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 1); 891f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, 892f5e7d251SAlgea Cao HDMI_PHY_I2CM_SLAVE_ADDR); 893f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 0); 894f5e7d251SAlgea Cao 895f5e7d251SAlgea Cao /* Write to the PHY as configured by the platform */ 896f5e7d251SAlgea Cao if (pdata->configure_phy) 897f5e7d251SAlgea Cao ret = pdata->configure_phy(hdmi, pdata, mpixelclock); 898f5e7d251SAlgea Cao else 899f5e7d251SAlgea Cao ret = phy->configure(hdmi, pdata, mpixelclock); 900f5e7d251SAlgea Cao if (ret) { 901f5e7d251SAlgea Cao printf("PHY configuration failed (clock %lu)\n", 902f5e7d251SAlgea Cao mpixelclock); 903f5e7d251SAlgea Cao return ret; 904f5e7d251SAlgea Cao } 905f5e7d251SAlgea Cao 906f5e7d251SAlgea Cao /* Wait for resuming transmission of TMDS clock and data */ 9078e2bab3fSAlgea Cao if (mtmdsclock > 340000000) 908f5e7d251SAlgea Cao mdelay(100); 909f5e7d251SAlgea Cao 910f5e7d251SAlgea Cao return dw_hdmi_phy_power_on(hdmi); 911f5e7d251SAlgea Cao } 912f5e7d251SAlgea Cao 9138e2bab3fSAlgea Cao static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, 9148e2bab3fSAlgea Cao void *data) 915f5e7d251SAlgea Cao { 916f5e7d251SAlgea Cao int i, ret; 917f5e7d251SAlgea Cao 918f5e7d251SAlgea Cao /* HDMI Phy spec says to do the phy initialization sequence twice */ 919f5e7d251SAlgea Cao for (i = 0; i < 2; i++) { 920f5e7d251SAlgea Cao dw_hdmi_phy_sel_data_en_pol(hdmi, 1); 921f5e7d251SAlgea Cao dw_hdmi_phy_sel_interface_control(hdmi, 0); 922f5e7d251SAlgea Cao ret = hdmi_phy_configure(hdmi); 923f5e7d251SAlgea Cao if (ret) 924f5e7d251SAlgea Cao return ret; 925f5e7d251SAlgea Cao } 926f5e7d251SAlgea Cao 927f5e7d251SAlgea Cao return 0; 928f5e7d251SAlgea Cao } 929f5e7d251SAlgea Cao 9308e2bab3fSAlgea Cao static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, 9318e2bab3fSAlgea Cao void *data) 932f5e7d251SAlgea Cao { 933f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi); 934f5e7d251SAlgea Cao } 935f5e7d251SAlgea Cao 9368e2bab3fSAlgea Cao static enum drm_connector_status 9378e2bab3fSAlgea Cao dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data) 938f5e7d251SAlgea Cao { 939f5e7d251SAlgea Cao return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? 940f5e7d251SAlgea Cao connector_status_connected : connector_status_disconnected; 941f5e7d251SAlgea Cao } 942f5e7d251SAlgea Cao 943f5e7d251SAlgea Cao static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { 944f5e7d251SAlgea Cao .init = dw_hdmi_phy_init, 945f5e7d251SAlgea Cao .disable = dw_hdmi_phy_disable, 946f5e7d251SAlgea Cao .read_hpd = dw_hdmi_phy_read_hpd, 947f5e7d251SAlgea Cao }; 948f5e7d251SAlgea Cao 949f5e7d251SAlgea Cao static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) 950f5e7d251SAlgea Cao { 951f5e7d251SAlgea Cao unsigned int i; 952f5e7d251SAlgea Cao u8 phy_type; 953f5e7d251SAlgea Cao 954f5e7d251SAlgea Cao phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); 955f5e7d251SAlgea Cao 956f5e7d251SAlgea Cao /* 957f5e7d251SAlgea Cao * RK3228 and RK3328 phy_type is DW_HDMI_PHY_DWC_HDMI20_TX_PHY, 958f5e7d251SAlgea Cao * but it has a vedor phy. 959f5e7d251SAlgea Cao */ 960f5e7d251SAlgea Cao if (phy_type == DW_HDMI_PHY_VENDOR_PHY || 961f5e7d251SAlgea Cao hdmi->dev_type == RK3328_HDMI || 962f5e7d251SAlgea Cao hdmi->dev_type == RK3228_HDMI) { 963f5e7d251SAlgea Cao /* Vendor PHYs require support from the glue layer. */ 964f5e7d251SAlgea Cao if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) { 965f5e7d251SAlgea Cao printf( 966f5e7d251SAlgea Cao "Vendor HDMI PHY not supported by glue layer\n"); 967f5e7d251SAlgea Cao return -ENODEV; 968f5e7d251SAlgea Cao } 969f5e7d251SAlgea Cao 970f5e7d251SAlgea Cao hdmi->phy.ops = hdmi->plat_data->phy_ops; 971f5e7d251SAlgea Cao hdmi->phy.data = hdmi->plat_data->phy_data; 972f5e7d251SAlgea Cao hdmi->phy.name = hdmi->plat_data->phy_name; 973f5e7d251SAlgea Cao return 0; 974f5e7d251SAlgea Cao } 975f5e7d251SAlgea Cao 976f5e7d251SAlgea Cao /* Synopsys PHYs are handled internally. */ 977f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { 978f5e7d251SAlgea Cao if (dw_hdmi_phys[i].type == phy_type) { 979f5e7d251SAlgea Cao hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops; 980f5e7d251SAlgea Cao hdmi->phy.name = dw_hdmi_phys[i].name; 981f5e7d251SAlgea Cao hdmi->phy.data = (void *)&dw_hdmi_phys[i]; 982f5e7d251SAlgea Cao 983f5e7d251SAlgea Cao if (!dw_hdmi_phys[i].configure && 984f5e7d251SAlgea Cao !hdmi->plat_data->configure_phy) { 985f5e7d251SAlgea Cao printf("%s requires platform support\n", 986f5e7d251SAlgea Cao hdmi->phy.name); 987f5e7d251SAlgea Cao return -ENODEV; 988f5e7d251SAlgea Cao } 989f5e7d251SAlgea Cao 990f5e7d251SAlgea Cao return 0; 991f5e7d251SAlgea Cao } 992f5e7d251SAlgea Cao } 993f5e7d251SAlgea Cao 994f5e7d251SAlgea Cao printf("Unsupported HDMI PHY type (%02x)\n", phy_type); 995f5e7d251SAlgea Cao return -ENODEV; 996f5e7d251SAlgea Cao } 997f5e7d251SAlgea Cao 9988e2bab3fSAlgea Cao static unsigned int 9998e2bab3fSAlgea Cao hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock) 10008e2bab3fSAlgea Cao { 10018e2bab3fSAlgea Cao unsigned int tmdsclock = mpixelclock; 10028e2bab3fSAlgea Cao unsigned int depth = 10038e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 10048e2bab3fSAlgea Cao 10058e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { 10068e2bab3fSAlgea Cao switch (depth) { 10078e2bab3fSAlgea Cao case 16: 10088e2bab3fSAlgea Cao tmdsclock = mpixelclock * 2; 10098e2bab3fSAlgea Cao break; 10108e2bab3fSAlgea Cao case 12: 10118e2bab3fSAlgea Cao tmdsclock = mpixelclock * 3 / 2; 10128e2bab3fSAlgea Cao break; 10138e2bab3fSAlgea Cao case 10: 10148e2bab3fSAlgea Cao tmdsclock = mpixelclock * 5 / 4; 10158e2bab3fSAlgea Cao break; 10168e2bab3fSAlgea Cao default: 10178e2bab3fSAlgea Cao break; 10188e2bab3fSAlgea Cao } 10198e2bab3fSAlgea Cao } 10208e2bab3fSAlgea Cao 10218e2bab3fSAlgea Cao return tmdsclock; 10228e2bab3fSAlgea Cao } 10238e2bab3fSAlgea Cao 1024f5e7d251SAlgea Cao static void hdmi_av_composer(struct dw_hdmi *hdmi, 1025f5e7d251SAlgea Cao const struct drm_display_mode *mode) 1026f5e7d251SAlgea Cao { 10278e2bab3fSAlgea Cao u8 bytes = 0, inv_val = 0; 1028f5e7d251SAlgea Cao struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; 1029f5e7d251SAlgea Cao struct drm_hdmi_info *hdmi_info = &hdmi->edid_data.display_info.hdmi; 10308e2bab3fSAlgea Cao int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; 1031f5e7d251SAlgea Cao unsigned int hdisplay, vdisplay; 1032f5e7d251SAlgea Cao 10338e2bab3fSAlgea Cao vmode->mpixelclock = mode->crtc_clock * 1000; 1034f5e7d251SAlgea Cao if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == 1035f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING) 1036f5e7d251SAlgea Cao vmode->mpixelclock *= 2; 10378e2bab3fSAlgea Cao vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); 10388e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 10398e2bab3fSAlgea Cao vmode->mtmdsclock /= 2; 10408e2bab3fSAlgea Cao printf("final pixclk = %d tmdsclk = %d\n", 10418e2bab3fSAlgea Cao vmode->mpixelclock, vmode->mtmdsclock); 1042f5e7d251SAlgea Cao 1043f5e7d251SAlgea Cao /* Set up HDMI_FC_INVIDCONF 1044f5e7d251SAlgea Cao * fc_invidconf.HDCP_keepout must be set (1'b1) 1045f5e7d251SAlgea Cao * when activate the scrambler feature. 1046f5e7d251SAlgea Cao */ 10478e2bab3fSAlgea Cao inv_val = (vmode->mtmdsclock > 340000000 || 10488e2bab3fSAlgea Cao (hdmi_info->scdc.scrambling.low_rates && 10498e2bab3fSAlgea Cao hdmi->scramble_low_rates) ? 1050f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : 1051f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); 1052f5e7d251SAlgea Cao 1053f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ? 1054f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : 1055f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW; 1056f5e7d251SAlgea Cao 1057f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ? 1058f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : 1059f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW; 1060f5e7d251SAlgea Cao 1061f5e7d251SAlgea Cao inv_val |= (vmode->mdataenablepolarity ? 1062f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : 1063f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); 1064f5e7d251SAlgea Cao 1065f5e7d251SAlgea Cao if (hdmi->vic == 39) 1066f5e7d251SAlgea Cao inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; 1067f5e7d251SAlgea Cao else 1068f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 1069f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : 1070f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW; 1071f5e7d251SAlgea Cao 1072f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 1073f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : 1074f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE; 1075f5e7d251SAlgea Cao 1076f5e7d251SAlgea Cao inv_val |= hdmi->sink_is_hdmi ? 1077f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : 1078f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE; 1079f5e7d251SAlgea Cao 1080f5e7d251SAlgea Cao hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); 1081f5e7d251SAlgea Cao 1082f5e7d251SAlgea Cao hdisplay = mode->hdisplay; 1083f5e7d251SAlgea Cao hblank = mode->htotal - mode->hdisplay; 1084f5e7d251SAlgea Cao h_de_hs = mode->hsync_start - mode->hdisplay; 1085f5e7d251SAlgea Cao hsync_len = mode->hsync_end - mode->hsync_start; 1086f5e7d251SAlgea Cao 1087f5e7d251SAlgea Cao /* 1088f5e7d251SAlgea Cao * When we're setting a YCbCr420 mode, we need 1089f5e7d251SAlgea Cao * to adjust the horizontal timing to suit. 1090f5e7d251SAlgea Cao */ 1091f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { 1092f5e7d251SAlgea Cao hdisplay /= 2; 1093f5e7d251SAlgea Cao hblank /= 2; 1094f5e7d251SAlgea Cao h_de_hs /= 2; 1095f5e7d251SAlgea Cao hsync_len /= 2; 1096f5e7d251SAlgea Cao } 1097f5e7d251SAlgea Cao 1098f5e7d251SAlgea Cao vdisplay = mode->vdisplay; 1099f5e7d251SAlgea Cao vblank = mode->vtotal - mode->vdisplay; 1100f5e7d251SAlgea Cao v_de_vs = mode->vsync_start - mode->vdisplay; 1101f5e7d251SAlgea Cao vsync_len = mode->vsync_end - mode->vsync_start; 1102f5e7d251SAlgea Cao 1103f5e7d251SAlgea Cao /* 1104f5e7d251SAlgea Cao * When we're setting an interlaced mode, we need 1105f5e7d251SAlgea Cao * to adjust the vertical timing to suit. 1106f5e7d251SAlgea Cao */ 1107f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 1108f5e7d251SAlgea Cao vdisplay /= 2; 1109f5e7d251SAlgea Cao vblank /= 2; 1110f5e7d251SAlgea Cao v_de_vs /= 2; 1111f5e7d251SAlgea Cao vsync_len /= 2; 1112f5e7d251SAlgea Cao } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == 1113f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING) { 1114f5e7d251SAlgea Cao vdisplay += mode->vtotal; 1115f5e7d251SAlgea Cao } 1116f5e7d251SAlgea Cao 1117f5e7d251SAlgea Cao /* Scrambling Control */ 1118f5e7d251SAlgea Cao if (hdmi_info->scdc.supported) { 11198e2bab3fSAlgea Cao if (vmode->mtmdsclock > 340000000 || 11208e2bab3fSAlgea Cao (hdmi_info->scdc.scrambling.low_rates && 11218e2bab3fSAlgea Cao hdmi->scramble_low_rates)) { 11228e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_SINK_VERSION, &bytes); 11238e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_SOURCE_VERSION, 11248e2bab3fSAlgea Cao bytes); 1125f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 1); 1126f5e7d251SAlgea Cao } else { 1127f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 0); 1128f5e7d251SAlgea Cao } 1129f5e7d251SAlgea Cao } 1130f5e7d251SAlgea Cao 1131f5e7d251SAlgea Cao /* Set up horizontal active pixel width */ 1132f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1); 1133f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0); 1134f5e7d251SAlgea Cao 1135f5e7d251SAlgea Cao /* Set up vertical active lines */ 1136f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1); 1137f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0); 1138f5e7d251SAlgea Cao 1139f5e7d251SAlgea Cao /* Set up horizontal blanking pixel region width */ 1140f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); 1141f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); 1142f5e7d251SAlgea Cao 1143f5e7d251SAlgea Cao /* Set up vertical blanking pixel region width */ 1144f5e7d251SAlgea Cao hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); 1145f5e7d251SAlgea Cao 1146f5e7d251SAlgea Cao /* Set up HSYNC active edge delay width (in pixel clks) */ 1147f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); 1148f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); 1149f5e7d251SAlgea Cao 1150f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */ 1151f5e7d251SAlgea Cao hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); 1152f5e7d251SAlgea Cao 1153f5e7d251SAlgea Cao /* Set up HSYNC active pulse width (in pixel clks) */ 1154f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); 1155f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); 1156f5e7d251SAlgea Cao 1157f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */ 1158f5e7d251SAlgea Cao hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); 1159f5e7d251SAlgea Cao } 1160f5e7d251SAlgea Cao 1161f5e7d251SAlgea Cao static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) 1162f5e7d251SAlgea Cao { 1163f5e7d251SAlgea Cao const u16 (*csc_coeff)[3][4] = &csc_coeff_default; 1164f5e7d251SAlgea Cao unsigned i; 1165f5e7d251SAlgea Cao u32 csc_scale = 1; 1166b5016cf2SAlgea Cao int enc_out_rgb, enc_in_rgb; 1167b5016cf2SAlgea Cao 1168b5016cf2SAlgea Cao enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); 1169b5016cf2SAlgea Cao enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); 1170f5e7d251SAlgea Cao 1171f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) { 11723f6d16abSAlgea Cao if (enc_out_rgb && enc_in_rgb) { 1173b5016cf2SAlgea Cao csc_coeff = &csc_coeff_full_to_limited; 1174b5016cf2SAlgea Cao csc_scale = 0; 1175b5016cf2SAlgea Cao } else if (enc_out_rgb) { 1176f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding == 1177f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601) 1178f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu601; 1179f5e7d251SAlgea Cao else 1180f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu709; 1181b5016cf2SAlgea Cao } else if (enc_in_rgb) { 1182f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding == 1183f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601) 1184f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu601; 1185f5e7d251SAlgea Cao else 1186f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu709; 1187f5e7d251SAlgea Cao csc_scale = 0; 1188f5e7d251SAlgea Cao } 1189f5e7d251SAlgea Cao } 1190f5e7d251SAlgea Cao 1191f5e7d251SAlgea Cao /* The CSC registers are sequential, alternating MSB then LSB */ 1192f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { 1193f5e7d251SAlgea Cao u16 coeff_a = (*csc_coeff)[0][i]; 1194f5e7d251SAlgea Cao u16 coeff_b = (*csc_coeff)[1][i]; 1195f5e7d251SAlgea Cao u16 coeff_c = (*csc_coeff)[2][i]; 1196f5e7d251SAlgea Cao 1197f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); 1198f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); 1199f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); 1200f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); 1201f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); 1202f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); 1203f5e7d251SAlgea Cao } 1204f5e7d251SAlgea Cao 1205f5e7d251SAlgea Cao hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, 1206f5e7d251SAlgea Cao HDMI_CSC_SCALE); 1207f5e7d251SAlgea Cao } 1208f5e7d251SAlgea Cao 1209f5e7d251SAlgea Cao static int is_color_space_interpolation(struct dw_hdmi *hdmi) 1210f5e7d251SAlgea Cao { 1211f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format)) 1212f5e7d251SAlgea Cao return 0; 1213f5e7d251SAlgea Cao 1214f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || 1215f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) 1216f5e7d251SAlgea Cao return 1; 1217f5e7d251SAlgea Cao 1218f5e7d251SAlgea Cao return 0; 1219f5e7d251SAlgea Cao } 1220f5e7d251SAlgea Cao 1221f5e7d251SAlgea Cao static void hdmi_video_csc(struct dw_hdmi *hdmi) 1222f5e7d251SAlgea Cao { 1223f5e7d251SAlgea Cao int color_depth = 0; 1224f5e7d251SAlgea Cao int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; 1225f5e7d251SAlgea Cao int decimation = 0; 1226f5e7d251SAlgea Cao 1227f5e7d251SAlgea Cao /* YCC422 interpolation to 444 mode */ 1228f5e7d251SAlgea Cao if (is_color_space_interpolation(hdmi)) 1229f5e7d251SAlgea Cao interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; 1230f5e7d251SAlgea Cao else if (is_color_space_decimation(hdmi)) 1231f5e7d251SAlgea Cao decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; 1232f5e7d251SAlgea Cao 1233f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { 1234f5e7d251SAlgea Cao case 8: 1235f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; 1236f5e7d251SAlgea Cao break; 1237f5e7d251SAlgea Cao case 10: 1238f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; 1239f5e7d251SAlgea Cao break; 1240f5e7d251SAlgea Cao case 12: 1241f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; 1242f5e7d251SAlgea Cao break; 1243f5e7d251SAlgea Cao case 16: 1244f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; 1245f5e7d251SAlgea Cao break; 1246f5e7d251SAlgea Cao 1247f5e7d251SAlgea Cao default: 1248f5e7d251SAlgea Cao return; 1249f5e7d251SAlgea Cao } 1250f5e7d251SAlgea Cao 1251f5e7d251SAlgea Cao /* Configure the CSC registers */ 1252f5e7d251SAlgea Cao hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); 1253f5e7d251SAlgea Cao hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, 1254f5e7d251SAlgea Cao HDMI_CSC_SCALE); 1255f5e7d251SAlgea Cao 1256f5e7d251SAlgea Cao dw_hdmi_update_csc_coeffs(hdmi); 1257f5e7d251SAlgea Cao } 1258f5e7d251SAlgea Cao 1259f5e7d251SAlgea Cao static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) 1260f5e7d251SAlgea Cao { 1261f5e7d251SAlgea Cao u8 clkdis; 1262f5e7d251SAlgea Cao 1263f5e7d251SAlgea Cao /* control period minimum duration */ 1264f5e7d251SAlgea Cao hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); 1265f5e7d251SAlgea Cao hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); 1266f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC); 1267f5e7d251SAlgea Cao 1268f5e7d251SAlgea Cao /* Set to fill TMDS data channels */ 1269f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM); 1270f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM); 1271f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); 1272f5e7d251SAlgea Cao 1273f5e7d251SAlgea Cao /* Enable pixel clock and tmds data path */ 1274f5e7d251SAlgea Cao clkdis = 0x7F; 1275f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; 1276f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1277f5e7d251SAlgea Cao 1278f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; 1279f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1280f5e7d251SAlgea Cao 1281f5e7d251SAlgea Cao /* Enable csc path */ 1282f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) { 1283f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; 1284f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1285f5e7d251SAlgea Cao } 1286f5e7d251SAlgea Cao 1287f5e7d251SAlgea Cao /* Enable pixel repetition path */ 1288f5e7d251SAlgea Cao if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) { 1289f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; 1290f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1291f5e7d251SAlgea Cao } 1292f5e7d251SAlgea Cao 1293f5e7d251SAlgea Cao /* Enable color space conversion if needed */ 1294f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) 1295f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH, 1296f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL); 1297f5e7d251SAlgea Cao else 1298f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS, 1299f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL); 1300f5e7d251SAlgea Cao } 1301f5e7d251SAlgea Cao 1302f5e7d251SAlgea Cao static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) 1303f5e7d251SAlgea Cao { 1304f5e7d251SAlgea Cao unsigned int count; 1305f5e7d251SAlgea Cao unsigned int i; 1306f5e7d251SAlgea Cao u8 val; 1307f5e7d251SAlgea Cao 1308f5e7d251SAlgea Cao /* 1309f5e7d251SAlgea Cao * Under some circumstances the Frame Composer arithmetic unit can miss 1310f5e7d251SAlgea Cao * an FC register write due to being busy processing the previous one. 1311f5e7d251SAlgea Cao * The issue can be worked around by issuing a TMDS software reset and 1312f5e7d251SAlgea Cao * then write one of the FC registers several times. 1313f5e7d251SAlgea Cao * 1314f5e7d251SAlgea Cao * The number of iterations matters and depends on the HDMI TX revision 1315f5e7d251SAlgea Cao * (and possibly on the platform). So far only i.MX6Q (v1.30a) and 1316f5e7d251SAlgea Cao * i.MX6DL (v1.31a) have been identified as needing the workaround, with 1317f5e7d251SAlgea Cao * 4 and 1 iterations respectively. 1318f5e7d251SAlgea Cao */ 1319f5e7d251SAlgea Cao 1320f5e7d251SAlgea Cao switch (hdmi->version) { 1321f5e7d251SAlgea Cao case 0x130a: 1322f5e7d251SAlgea Cao count = 4; 1323f5e7d251SAlgea Cao break; 1324f5e7d251SAlgea Cao case 0x131a: 13258e2bab3fSAlgea Cao case 0x200a: 13268e2bab3fSAlgea Cao case 0x201a: 13278e2bab3fSAlgea Cao case 0x211a: 1328f5e7d251SAlgea Cao count = 1; 1329f5e7d251SAlgea Cao break; 1330f5e7d251SAlgea Cao default: 1331f5e7d251SAlgea Cao return; 1332f5e7d251SAlgea Cao } 1333f5e7d251SAlgea Cao 1334f5e7d251SAlgea Cao /* TMDS software reset */ 1335f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); 1336f5e7d251SAlgea Cao 1337f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); 1338f5e7d251SAlgea Cao for (i = 0; i < count; i++) 1339f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); 1340f5e7d251SAlgea Cao } 1341f5e7d251SAlgea Cao 1342f5e7d251SAlgea Cao static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) 1343f5e7d251SAlgea Cao { 1344f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, 1345f5e7d251SAlgea Cao HDMI_IH_MUTE_FC_STAT2); 1346f5e7d251SAlgea Cao } 1347f5e7d251SAlgea Cao 1348f5e7d251SAlgea Cao static void hdmi_video_packetize(struct dw_hdmi *hdmi) 1349f5e7d251SAlgea Cao { 1350f5e7d251SAlgea Cao unsigned int color_depth = 0; 1351f5e7d251SAlgea Cao unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; 1352f5e7d251SAlgea Cao unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; 1353f5e7d251SAlgea Cao struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; 1354f5e7d251SAlgea Cao u8 val, vp_conf; 1355f5e7d251SAlgea Cao 1356f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || 1357f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || 1358f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { 1359f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth( 1360f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) { 1361f5e7d251SAlgea Cao case 8: 1362f5e7d251SAlgea Cao color_depth = 0; 1363f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; 1364f5e7d251SAlgea Cao break; 1365f5e7d251SAlgea Cao case 10: 1366f5e7d251SAlgea Cao color_depth = 5; 1367f5e7d251SAlgea Cao break; 1368f5e7d251SAlgea Cao case 12: 1369f5e7d251SAlgea Cao color_depth = 6; 1370f5e7d251SAlgea Cao break; 1371f5e7d251SAlgea Cao case 16: 1372f5e7d251SAlgea Cao color_depth = 7; 1373f5e7d251SAlgea Cao break; 1374f5e7d251SAlgea Cao default: 1375f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; 1376f5e7d251SAlgea Cao } 1377f5e7d251SAlgea Cao } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { 1378f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth( 1379f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) { 1380f5e7d251SAlgea Cao case 0: 1381f5e7d251SAlgea Cao case 8: 1382f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_16bit; 1383f5e7d251SAlgea Cao break; 1384f5e7d251SAlgea Cao case 10: 1385f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_20bit; 1386f5e7d251SAlgea Cao break; 1387f5e7d251SAlgea Cao case 12: 1388f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_24bit; 1389f5e7d251SAlgea Cao break; 1390f5e7d251SAlgea Cao 1391f5e7d251SAlgea Cao default: 1392f5e7d251SAlgea Cao return; 1393f5e7d251SAlgea Cao } 1394f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; 1395f5e7d251SAlgea Cao } else { 1396f5e7d251SAlgea Cao return; 1397f5e7d251SAlgea Cao } 1398f5e7d251SAlgea Cao 1399f5e7d251SAlgea Cao /* set the packetizer registers */ 14008e2bab3fSAlgea Cao val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & 14018e2bab3fSAlgea Cao HDMI_VP_PR_CD_COLOR_DEPTH_MASK; 1402f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); 1403f5e7d251SAlgea Cao 1404f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, 1405f5e7d251SAlgea Cao HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); 1406f5e7d251SAlgea Cao 1407f5e7d251SAlgea Cao /* Data from pixel repeater block */ 1408f5e7d251SAlgea Cao if (hdmi_data->pix_repet_factor > 0) { 1409f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | 1410f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; 1411f5e7d251SAlgea Cao } else { /* data from packetizer block */ 1412f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | 1413f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; 1414f5e7d251SAlgea Cao } 1415f5e7d251SAlgea Cao 1416f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf, 1417f5e7d251SAlgea Cao HDMI_VP_CONF_PR_EN_MASK | 1418f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); 1419f5e7d251SAlgea Cao 14208e2bab3fSAlgea Cao if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) || 14218e2bab3fSAlgea Cao (color_depth == 6 && hdmi->previous_mode.htotal % 2)) 14228e2bab3fSAlgea Cao hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, 14238e2bab3fSAlgea Cao HDMI_VP_STUFF); 14248e2bab3fSAlgea Cao else 1425f5e7d251SAlgea Cao hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, 1426f5e7d251SAlgea Cao HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); 1427f5e7d251SAlgea Cao 1428f5e7d251SAlgea Cao hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); 1429f5e7d251SAlgea Cao 1430f5e7d251SAlgea Cao if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { 1431f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | 1432f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_ENABLE | 1433f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE; 1434f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { 1435f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | 1436f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE | 1437f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_ENABLE; 1438f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { 1439f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | 1440f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE | 1441f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE; 1442f5e7d251SAlgea Cao } else { 1443f5e7d251SAlgea Cao return; 1444f5e7d251SAlgea Cao } 1445f5e7d251SAlgea Cao 1446f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf, 1447f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | 1448f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); 1449f5e7d251SAlgea Cao 1450f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | 1451f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, 1452f5e7d251SAlgea Cao HDMI_VP_STUFF_PP_STUFFING_MASK | 1453f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); 1454f5e7d251SAlgea Cao 1455f5e7d251SAlgea Cao hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, 1456f5e7d251SAlgea Cao HDMI_VP_CONF); 1457f5e7d251SAlgea Cao } 1458f5e7d251SAlgea Cao 1459f5e7d251SAlgea Cao static void hdmi_video_sample(struct dw_hdmi *hdmi) 1460f5e7d251SAlgea Cao { 1461f5e7d251SAlgea Cao int color_format = 0; 1462f5e7d251SAlgea Cao u8 val; 1463f5e7d251SAlgea Cao 1464f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_in_bus_format) { 1465f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 1466f5e7d251SAlgea Cao color_format = 0x01; 1467f5e7d251SAlgea Cao break; 1468f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 1469f5e7d251SAlgea Cao color_format = 0x03; 1470f5e7d251SAlgea Cao break; 1471f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 1472f5e7d251SAlgea Cao color_format = 0x05; 1473f5e7d251SAlgea Cao break; 1474f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 1475f5e7d251SAlgea Cao color_format = 0x07; 1476f5e7d251SAlgea Cao break; 1477f5e7d251SAlgea Cao 1478f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 1479f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 1480f5e7d251SAlgea Cao color_format = 0x09; 1481f5e7d251SAlgea Cao break; 1482f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 1483f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 1484f5e7d251SAlgea Cao color_format = 0x0B; 1485f5e7d251SAlgea Cao break; 1486f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 1487f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 1488f5e7d251SAlgea Cao color_format = 0x0D; 1489f5e7d251SAlgea Cao break; 1490f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 1491f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 1492f5e7d251SAlgea Cao color_format = 0x0F; 1493f5e7d251SAlgea Cao break; 1494f5e7d251SAlgea Cao 1495f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 1496f5e7d251SAlgea Cao color_format = 0x16; 1497f5e7d251SAlgea Cao break; 1498f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 1499f5e7d251SAlgea Cao color_format = 0x14; 1500f5e7d251SAlgea Cao break; 1501f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 1502f5e7d251SAlgea Cao color_format = 0x12; 1503f5e7d251SAlgea Cao break; 1504f5e7d251SAlgea Cao 1505f5e7d251SAlgea Cao default: 1506f5e7d251SAlgea Cao return; 1507f5e7d251SAlgea Cao } 1508f5e7d251SAlgea Cao 1509f5e7d251SAlgea Cao val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | 1510f5e7d251SAlgea Cao ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & 1511f5e7d251SAlgea Cao HDMI_TX_INVID0_VIDEO_MAPPING_MASK); 1512f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INVID0); 1513f5e7d251SAlgea Cao 1514f5e7d251SAlgea Cao /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */ 1515f5e7d251SAlgea Cao val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | 1516f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | 1517f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; 1518f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING); 1519f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0); 1520f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1); 1521f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0); 1522f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1); 1523f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0); 1524f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1); 1525f5e7d251SAlgea Cao } 1526f5e7d251SAlgea Cao 15278e2bab3fSAlgea Cao static void dw_hdmi_disable(struct dw_hdmi *hdmi, struct display_state *state) 1528f5e7d251SAlgea Cao { 1529f5e7d251SAlgea Cao if (hdmi->phy.enabled) { 15308e2bab3fSAlgea Cao hdmi->phy.ops->disable(hdmi, state); 1531f5e7d251SAlgea Cao hdmi->phy.enabled = false; 1532f5e7d251SAlgea Cao } 1533f5e7d251SAlgea Cao } 1534f5e7d251SAlgea Cao 1535f5e7d251SAlgea Cao static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) 1536f5e7d251SAlgea Cao { 1537f5e7d251SAlgea Cao struct hdmi_avi_infoframe frame; 1538f5e7d251SAlgea Cao u8 val; 1539f5e7d251SAlgea Cao bool is_hdmi2 = false; 1540b5016cf2SAlgea Cao enum hdmi_quantization_range rgb_quant_range = 1541b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range; 1542f5e7d251SAlgea Cao 1543f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || 1544f5e7d251SAlgea Cao hdmi->edid_data.display_info.hdmi.scdc.supported) 1545f5e7d251SAlgea Cao is_hdmi2 = true; 1546f5e7d251SAlgea Cao /* Initialise info frame from DRM mode */ 1547f5e7d251SAlgea Cao drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2); 1548f5e7d251SAlgea Cao 1549b5016cf2SAlgea Cao /* 1550b5016cf2SAlgea Cao * Ignore monitor selectable quantization, use quantization set 1551b5016cf2SAlgea Cao * by the user 1552b5016cf2SAlgea Cao */ 1553b5016cf2SAlgea Cao drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range, 1554b5016cf2SAlgea Cao true); 1555f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) 1556f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV444; 1557f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 1558f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV422; 1559f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 1560f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV420; 1561f5e7d251SAlgea Cao else 1562f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_RGB; 1563f5e7d251SAlgea Cao 1564f5e7d251SAlgea Cao /* Set up colorimetry */ 1565f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_out_encoding) { 1566f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_601: 1567f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) 1568f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; 1569f5e7d251SAlgea Cao else 1570f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601; 1571f5e7d251SAlgea Cao frame.extended_colorimetry = 1572f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 1573f5e7d251SAlgea Cao break; 1574f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_709: 1575f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) 1576f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; 1577f5e7d251SAlgea Cao else 1578f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_709; 1579f5e7d251SAlgea Cao frame.extended_colorimetry = 1580f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; 1581f5e7d251SAlgea Cao break; 1582f5e7d251SAlgea Cao default: /* Carries no data */ 1583f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601; 1584f5e7d251SAlgea Cao frame.extended_colorimetry = 1585f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 1586f5e7d251SAlgea Cao break; 1587f5e7d251SAlgea Cao } 1588f5e7d251SAlgea Cao 1589f5e7d251SAlgea Cao frame.scan_mode = HDMI_SCAN_MODE_NONE; 1590f5e7d251SAlgea Cao 1591f5e7d251SAlgea Cao /* 1592f5e7d251SAlgea Cao * The Designware IP uses a different byte format from standard 1593f5e7d251SAlgea Cao * AVI info frames, though generally the bits are in the correct 1594f5e7d251SAlgea Cao * bytes. 1595f5e7d251SAlgea Cao */ 1596f5e7d251SAlgea Cao 1597f5e7d251SAlgea Cao /* 1598f5e7d251SAlgea Cao * AVI data byte 1 differences: Colorspace in bits 0,1,7 rather than 1599f5e7d251SAlgea Cao * 5,6,7, active aspect present in bit 6 rather than 4. 1600f5e7d251SAlgea Cao */ 1601f5e7d251SAlgea Cao val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 0x3); 1602f5e7d251SAlgea Cao if (frame.active_aspect & 15) 1603f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT; 1604f5e7d251SAlgea Cao if (frame.top_bar || frame.bottom_bar) 1605f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR; 1606f5e7d251SAlgea Cao if (frame.left_bar || frame.right_bar) 1607f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR; 1608f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0); 1609f5e7d251SAlgea Cao 1610f5e7d251SAlgea Cao /* AVI data byte 2 differences: none */ 1611f5e7d251SAlgea Cao val = ((frame.colorimetry & 0x3) << 6) | 1612f5e7d251SAlgea Cao ((frame.picture_aspect & 0x3) << 4) | 1613f5e7d251SAlgea Cao (frame.active_aspect & 0xf); 1614f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1); 1615f5e7d251SAlgea Cao 1616f5e7d251SAlgea Cao /* AVI data byte 3 differences: none */ 1617f5e7d251SAlgea Cao val = ((frame.extended_colorimetry & 0x7) << 4) | 1618f5e7d251SAlgea Cao ((frame.quantization_range & 0x3) << 2) | 1619f5e7d251SAlgea Cao (frame.nups & 0x3); 1620f5e7d251SAlgea Cao if (frame.itc) 1621f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID; 1622f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); 1623f5e7d251SAlgea Cao 1624f5e7d251SAlgea Cao /* AVI data byte 4 differences: none */ 1625f5e7d251SAlgea Cao val = frame.video_code & 0x7f; 1626f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVIVID); 1627f5e7d251SAlgea Cao 1628f5e7d251SAlgea Cao /* AVI Data Byte 5- set up input and output pixel repetition */ 1629f5e7d251SAlgea Cao val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) << 1630f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) & 1631f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) | 1632f5e7d251SAlgea Cao ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput << 1633f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) & 1634f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK); 1635f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_PRCONF); 1636f5e7d251SAlgea Cao 1637f5e7d251SAlgea Cao /* 1638f5e7d251SAlgea Cao * AVI data byte 5 differences: content type in 0,1 rather than 4,5, 1639f5e7d251SAlgea Cao * ycc range in bits 2,3 rather than 6,7 1640f5e7d251SAlgea Cao */ 1641f5e7d251SAlgea Cao val = ((frame.ycc_quantization_range & 0x3) << 2) | 1642f5e7d251SAlgea Cao (frame.content_type & 0x3); 1643f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3); 1644f5e7d251SAlgea Cao 1645f5e7d251SAlgea Cao /* AVI Data Bytes 6-13 */ 1646f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0); 1647f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1); 1648f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0); 1649f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1); 1650f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0); 1651f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1); 1652f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0); 1653f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1); 1654f5e7d251SAlgea Cao } 1655f5e7d251SAlgea Cao 1656f5e7d251SAlgea Cao static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, 1657f5e7d251SAlgea Cao struct drm_display_mode *mode) 1658f5e7d251SAlgea Cao { 1659f5e7d251SAlgea Cao struct hdmi_vendor_infoframe frame; 1660f5e7d251SAlgea Cao u8 buffer[10]; 1661f5e7d251SAlgea Cao ssize_t err; 1662f5e7d251SAlgea Cao 1663f5e7d251SAlgea Cao /* Disable HDMI vendor specific infoframe send */ 1664f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, 1665f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK); 1666f5e7d251SAlgea Cao 1667f5e7d251SAlgea Cao err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode); 1668f5e7d251SAlgea Cao if (err < 0) 1669f5e7d251SAlgea Cao /* 1670f5e7d251SAlgea Cao * Going into that statement does not means vendor infoframe 1671f5e7d251SAlgea Cao * fails. It just informed us that vendor infoframe is not 1672f5e7d251SAlgea Cao * needed for the selected mode. Only 4k or stereoscopic 3D 1673f5e7d251SAlgea Cao * mode requires vendor infoframe. So just simply return. 1674f5e7d251SAlgea Cao */ 1675f5e7d251SAlgea Cao return; 1676f5e7d251SAlgea Cao 1677f5e7d251SAlgea Cao err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 1678f5e7d251SAlgea Cao if (err < 0) { 1679f5e7d251SAlgea Cao printf("Failed to pack vendor infoframe: %zd\n", err); 1680f5e7d251SAlgea Cao return; 1681f5e7d251SAlgea Cao } 1682f5e7d251SAlgea Cao 1683f5e7d251SAlgea Cao /* Set the length of HDMI vendor specific InfoFrame payload */ 1684f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE); 1685f5e7d251SAlgea Cao 1686f5e7d251SAlgea Cao /* Set 24bit IEEE Registration Identifier */ 1687f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0); 1688f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1); 1689f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2); 1690f5e7d251SAlgea Cao 1691f5e7d251SAlgea Cao /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */ 1692f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0); 1693f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1); 1694f5e7d251SAlgea Cao 1695f5e7d251SAlgea Cao if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) 1696f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2); 1697f5e7d251SAlgea Cao 1698f5e7d251SAlgea Cao /* Packet frame interpolation */ 1699f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1); 1700f5e7d251SAlgea Cao 1701f5e7d251SAlgea Cao /* Auto packets per frame and line spacing */ 1702f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2); 1703f5e7d251SAlgea Cao 1704f5e7d251SAlgea Cao /* Configures the Frame Composer On RDRB mode */ 1705f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, 1706f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK); 1707f5e7d251SAlgea Cao } 1708f5e7d251SAlgea Cao 1709f5e7d251SAlgea Cao static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, 1710f5e7d251SAlgea Cao unsigned int n) 1711f5e7d251SAlgea Cao { 1712f5e7d251SAlgea Cao /* Must be set/cleared first */ 1713f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); 1714f5e7d251SAlgea Cao 1715f5e7d251SAlgea Cao /* nshift factor = 0 */ 1716f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); 1717f5e7d251SAlgea Cao 1718f5e7d251SAlgea Cao hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | 1719f5e7d251SAlgea Cao HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); 1720f5e7d251SAlgea Cao hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); 1721f5e7d251SAlgea Cao hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); 1722f5e7d251SAlgea Cao 1723f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3); 1724f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); 1725f5e7d251SAlgea Cao hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); 1726f5e7d251SAlgea Cao } 1727f5e7d251SAlgea Cao 1728f5e7d251SAlgea Cao static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi, 1729f5e7d251SAlgea Cao unsigned long pixel_clk, 1730f5e7d251SAlgea Cao unsigned long freq) 1731f5e7d251SAlgea Cao { 1732f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; 1733f5e7d251SAlgea Cao const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; 1734f5e7d251SAlgea Cao int i; 1735f5e7d251SAlgea Cao 1736f5e7d251SAlgea Cao if (plat_data->tmds_n_table) { 1737f5e7d251SAlgea Cao for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { 1738f5e7d251SAlgea Cao if (pixel_clk == plat_data->tmds_n_table[i].tmds) { 1739f5e7d251SAlgea Cao tmds_n = &plat_data->tmds_n_table[i]; 1740f5e7d251SAlgea Cao break; 1741f5e7d251SAlgea Cao } 1742f5e7d251SAlgea Cao } 1743f5e7d251SAlgea Cao } 1744f5e7d251SAlgea Cao 1745f5e7d251SAlgea Cao if (!tmds_n) { 1746f5e7d251SAlgea Cao for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { 1747f5e7d251SAlgea Cao if (pixel_clk == common_tmds_n_table[i].tmds) { 1748f5e7d251SAlgea Cao tmds_n = &common_tmds_n_table[i]; 1749f5e7d251SAlgea Cao break; 1750f5e7d251SAlgea Cao } 1751f5e7d251SAlgea Cao } 1752f5e7d251SAlgea Cao } 1753f5e7d251SAlgea Cao 1754f5e7d251SAlgea Cao if (!tmds_n) 1755f5e7d251SAlgea Cao return -ENOENT; 1756f5e7d251SAlgea Cao 1757f5e7d251SAlgea Cao switch (freq) { 1758f5e7d251SAlgea Cao case 32000: 1759f5e7d251SAlgea Cao return tmds_n->n_32k; 1760f5e7d251SAlgea Cao case 44100: 1761f5e7d251SAlgea Cao case 88200: 1762f5e7d251SAlgea Cao case 176400: 1763f5e7d251SAlgea Cao return (freq / 44100) * tmds_n->n_44k1; 1764f5e7d251SAlgea Cao case 48000: 1765f5e7d251SAlgea Cao case 96000: 1766f5e7d251SAlgea Cao case 192000: 1767f5e7d251SAlgea Cao return (freq / 48000) * tmds_n->n_48k; 1768f5e7d251SAlgea Cao default: 1769f5e7d251SAlgea Cao return -ENOENT; 1770f5e7d251SAlgea Cao } 1771f5e7d251SAlgea Cao } 1772f5e7d251SAlgea Cao 1773f5e7d251SAlgea Cao static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, 1774f5e7d251SAlgea Cao unsigned int pixel_clk) 1775f5e7d251SAlgea Cao { 1776f5e7d251SAlgea Cao u64 final, diff; 1777f5e7d251SAlgea Cao u64 cts; 1778f5e7d251SAlgea Cao 1779f5e7d251SAlgea Cao final = (u64)pixel_clk * n; 1780f5e7d251SAlgea Cao 1781f5e7d251SAlgea Cao cts = final; 1782f5e7d251SAlgea Cao do_div(cts, 128 * freq); 1783f5e7d251SAlgea Cao 1784f5e7d251SAlgea Cao diff = final - (u64)cts * (128 * freq); 1785f5e7d251SAlgea Cao 1786f5e7d251SAlgea Cao return diff; 1787f5e7d251SAlgea Cao } 1788f5e7d251SAlgea Cao 1789f5e7d251SAlgea Cao static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi, 1790f5e7d251SAlgea Cao unsigned long pixel_clk, 1791f5e7d251SAlgea Cao unsigned long freq) 1792f5e7d251SAlgea Cao { 1793f5e7d251SAlgea Cao unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); 1794f5e7d251SAlgea Cao unsigned int max_n = (128 * freq) / 300; 1795f5e7d251SAlgea Cao unsigned int ideal_n = (128 * freq) / 1000; 1796f5e7d251SAlgea Cao unsigned int best_n_distance = ideal_n; 1797f5e7d251SAlgea Cao unsigned int best_n = 0; 1798f5e7d251SAlgea Cao u64 best_diff = U64_MAX; 1799f5e7d251SAlgea Cao int n; 1800f5e7d251SAlgea Cao 1801f5e7d251SAlgea Cao /* If the ideal N could satisfy the audio math, then just take it */ 1802f5e7d251SAlgea Cao if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) 1803f5e7d251SAlgea Cao return ideal_n; 1804f5e7d251SAlgea Cao 1805f5e7d251SAlgea Cao for (n = min_n; n <= max_n; n++) { 1806f5e7d251SAlgea Cao u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); 1807f5e7d251SAlgea Cao 1808f5e7d251SAlgea Cao if (diff < best_diff || (diff == best_diff && 1809f5e7d251SAlgea Cao abs(n - ideal_n) < best_n_distance)) { 1810f5e7d251SAlgea Cao best_n = n; 1811f5e7d251SAlgea Cao best_diff = diff; 1812f5e7d251SAlgea Cao best_n_distance = abs(best_n - ideal_n); 1813f5e7d251SAlgea Cao } 1814f5e7d251SAlgea Cao 1815f5e7d251SAlgea Cao /* 1816f5e7d251SAlgea Cao * The best N already satisfy the audio math, and also be 1817f5e7d251SAlgea Cao * the closest value to ideal N, so just cut the loop. 1818f5e7d251SAlgea Cao */ 1819f5e7d251SAlgea Cao if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) 1820f5e7d251SAlgea Cao break; 1821f5e7d251SAlgea Cao } 1822f5e7d251SAlgea Cao 1823f5e7d251SAlgea Cao return best_n; 1824f5e7d251SAlgea Cao } 1825f5e7d251SAlgea Cao 1826f5e7d251SAlgea Cao static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk, 1827f5e7d251SAlgea Cao unsigned long sample_rate) 1828f5e7d251SAlgea Cao { 1829f5e7d251SAlgea Cao int n; 1830f5e7d251SAlgea Cao 1831f5e7d251SAlgea Cao n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); 1832f5e7d251SAlgea Cao if (n > 0) 1833f5e7d251SAlgea Cao return n; 1834f5e7d251SAlgea Cao 1835f5e7d251SAlgea Cao printf("Rate %lu missing; compute N dynamically\n", 1836f5e7d251SAlgea Cao pixel_clk); 1837f5e7d251SAlgea Cao 1838f5e7d251SAlgea Cao return hdmi_compute_n(hdmi, pixel_clk, sample_rate); 1839f5e7d251SAlgea Cao } 1840f5e7d251SAlgea Cao 1841f5e7d251SAlgea Cao static 1842f5e7d251SAlgea Cao void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, unsigned long pixel_clk, 1843f5e7d251SAlgea Cao unsigned int sample_rate) 1844f5e7d251SAlgea Cao { 1845f5e7d251SAlgea Cao unsigned long ftdms = pixel_clk; 1846f5e7d251SAlgea Cao unsigned int n, cts; 1847f5e7d251SAlgea Cao u64 tmp; 1848f5e7d251SAlgea Cao 1849f5e7d251SAlgea Cao n = hdmi_find_n(hdmi, pixel_clk, sample_rate); 1850f5e7d251SAlgea Cao 1851f5e7d251SAlgea Cao /* 1852f5e7d251SAlgea Cao * Compute the CTS value from the N value. Note that CTS and N 1853f5e7d251SAlgea Cao * can be up to 20 bits in total, so we need 64-bit math. Also 1854f5e7d251SAlgea Cao * note that our TDMS clock is not fully accurate; it is accurate 1855f5e7d251SAlgea Cao * to kHz. This can introduce an unnecessary remainder in the 1856f5e7d251SAlgea Cao * calculation below, so we don't try to warn about that. 1857f5e7d251SAlgea Cao */ 1858f5e7d251SAlgea Cao tmp = (u64)ftdms * n; 1859f5e7d251SAlgea Cao do_div(tmp, 128 * sample_rate); 1860f5e7d251SAlgea Cao cts = tmp; 1861f5e7d251SAlgea Cao 1862f5e7d251SAlgea Cao printf("%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", __func__, 1863f5e7d251SAlgea Cao sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, n, cts); 1864f5e7d251SAlgea Cao 1865f5e7d251SAlgea Cao hdmi->audio_n = n; 1866f5e7d251SAlgea Cao hdmi->audio_cts = cts; 1867f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0); 1868f5e7d251SAlgea Cao } 1869f5e7d251SAlgea Cao 1870f5e7d251SAlgea Cao static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) 1871f5e7d251SAlgea Cao { 18728e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, 1873f5e7d251SAlgea Cao hdmi->sample_rate); 1874f5e7d251SAlgea Cao } 1875f5e7d251SAlgea Cao 1876f5e7d251SAlgea Cao static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) 1877f5e7d251SAlgea Cao { 1878f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); 1879f5e7d251SAlgea Cao } 1880f5e7d251SAlgea Cao 1881f5e7d251SAlgea Cao void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) 1882f5e7d251SAlgea Cao { 1883f5e7d251SAlgea Cao hdmi->sample_rate = rate; 18848e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, 1885f5e7d251SAlgea Cao hdmi->sample_rate); 1886f5e7d251SAlgea Cao } 1887f5e7d251SAlgea Cao 18888e2bab3fSAlgea Cao static int dw_hdmi_hdcp_load_key(struct dw_hdmi *hdmi) 18898e2bab3fSAlgea Cao { 18908e2bab3fSAlgea Cao int i, j, ret, val; 18918e2bab3fSAlgea Cao struct hdcp_keys *hdcp_keys; 18928e2bab3fSAlgea Cao 18938e2bab3fSAlgea Cao val = sizeof(*hdcp_keys); 18948e2bab3fSAlgea Cao hdcp_keys = malloc(val); 18958e2bab3fSAlgea Cao if (!hdcp_keys) 18968e2bab3fSAlgea Cao return -ENOMEM; 18978e2bab3fSAlgea Cao 18988e2bab3fSAlgea Cao memset(hdcp_keys, 0, val); 18998e2bab3fSAlgea Cao 19008e2bab3fSAlgea Cao ret = vendor_storage_read(HDMI_HDCP1X_ID, hdcp_keys, val); 19018e2bab3fSAlgea Cao if (ret < val) { 19028e2bab3fSAlgea Cao printf("HDCP: read size %d\n", ret); 19038e2bab3fSAlgea Cao free(hdcp_keys); 19048e2bab3fSAlgea Cao return -EINVAL; 19058e2bab3fSAlgea Cao } 19068e2bab3fSAlgea Cao 19078e2bab3fSAlgea Cao if (hdcp_keys->KSV[0] == 0x00 && 19088e2bab3fSAlgea Cao hdcp_keys->KSV[1] == 0x00 && 19098e2bab3fSAlgea Cao hdcp_keys->KSV[2] == 0x00 && 19108e2bab3fSAlgea Cao hdcp_keys->KSV[3] == 0x00 && 19118e2bab3fSAlgea Cao hdcp_keys->KSV[4] == 0x00) { 19128e2bab3fSAlgea Cao printf("HDCP: Invalid hdcp key\n"); 19138e2bab3fSAlgea Cao free(hdcp_keys); 19148e2bab3fSAlgea Cao return -EINVAL; 19158e2bab3fSAlgea Cao } 19168e2bab3fSAlgea Cao 19178e2bab3fSAlgea Cao /* Disable decryption logic */ 19188e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_RMCTL); 19198e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */ 19208e2bab3fSAlgea Cao do { 19218e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 19228e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 19238e2bab3fSAlgea Cao 19248e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK6); 19258e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK5); 19268e2bab3fSAlgea Cao 19278e2bab3fSAlgea Cao /* The useful data in ksv should be 5 byte */ 19288e2bab3fSAlgea Cao for (i = 4; i >= 0; i--) 19298e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->KSV[i], HDMI_HDCPREG_DPK0 + i); 19308e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */ 19318e2bab3fSAlgea Cao do { 19328e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 19338e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 19348e2bab3fSAlgea Cao 19358e2bab3fSAlgea Cao /* Enable decryption logic */ 19368e2bab3fSAlgea Cao hdmi_writeb(hdmi, 1, HDMI_HDCPREG_RMCTL); 19378e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[0], HDMI_HDCPREG_SEED1); 19388e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[1], HDMI_HDCPREG_SEED0); 19398e2bab3fSAlgea Cao 19408e2bab3fSAlgea Cao /* Write encrypt device private key */ 19418e2bab3fSAlgea Cao for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) { 19428e2bab3fSAlgea Cao for (j = 6; j >= 0; j--) 19438e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->devicekey[i + j], 19448e2bab3fSAlgea Cao HDMI_HDCPREG_DPK0 + j); 19458e2bab3fSAlgea Cao do { 19468e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 19478e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 19488e2bab3fSAlgea Cao } 19498e2bab3fSAlgea Cao 19508e2bab3fSAlgea Cao free(hdcp_keys); 19518e2bab3fSAlgea Cao return 0; 19528e2bab3fSAlgea Cao } 19538e2bab3fSAlgea Cao 19548e2bab3fSAlgea Cao static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi, 19558e2bab3fSAlgea Cao const struct drm_display_mode *mode) 19568e2bab3fSAlgea Cao { 19578e2bab3fSAlgea Cao u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi; 19588e2bab3fSAlgea Cao 19598e2bab3fSAlgea Cao if (!hdmi->hdcp1x_enable) 19608e2bab3fSAlgea Cao return; 19618e2bab3fSAlgea Cao 19628e2bab3fSAlgea Cao /* Configure the video polarity */ 19638e2bab3fSAlgea Cao vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ? 19648e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH : 19658e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW; 19668e2bab3fSAlgea Cao hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ? 19678e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH : 19688e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW; 19698e2bab3fSAlgea Cao data_pol = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; 19708e2bab3fSAlgea Cao hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol, 19718e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_MASK | 19728e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_MASK | 19738e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_DATAENPOL_MASK, 19748e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG); 19758e2bab3fSAlgea Cao 19768e2bab3fSAlgea Cao /* Config the display mode */ 19778e2bab3fSAlgea Cao hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI : 19788e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_HDMIDVI_DVI; 19798e2bab3fSAlgea Cao hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK, 19808e2bab3fSAlgea Cao HDMI_A_HDCPCFG0); 19818e2bab3fSAlgea Cao 19828e2bab3fSAlgea Cao if (!(hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS) & 0x3f)) 19838e2bab3fSAlgea Cao dw_hdmi_hdcp_load_key(hdmi); 19848e2bab3fSAlgea Cao 19858e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE, 19868e2bab3fSAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK, 19878e2bab3fSAlgea Cao HDMI_FC_INVIDCONF); 19888e2bab3fSAlgea Cao 19898e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_A_HDCP22_MASK) { 19908e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_HDCP2_OVR_ENABLE | 19918e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_DISABLE, 19928e2bab3fSAlgea Cao HDMI_HDCP2_OVR_EN_MASK | 19938e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_MASK, 19948e2bab3fSAlgea Cao HDMI_HDCP2REG_CTRL); 19958e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MASK); 19968e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MUTE); 19978e2bab3fSAlgea Cao } 19988e2bab3fSAlgea Cao 19998e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x40, HDMI_A_OESSWCFG); 20008e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE | 20018e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE | 20028e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE, 20038e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK | 20048e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_MASK | 20058e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_MASK, HDMI_A_HDCPCFG0); 20068e2bab3fSAlgea Cao 20078e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE | 20088e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE, 20098e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK | 20108e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK, HDMI_A_HDCPCFG1); 20118e2bab3fSAlgea Cao 20128e2bab3fSAlgea Cao /* Reset HDCP Engine */ 20138e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_HDCPCLK_MASK) { 20148e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_SWRESET_ASSERT, 20158e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_SWRESET_MASK, HDMI_A_HDCPCFG1); 20168e2bab3fSAlgea Cao } 20178e2bab3fSAlgea Cao 20188e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK); 20198e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_ENABLE, 20208e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); 20218e2bab3fSAlgea Cao 20228e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_MC_CLKDIS_HDCPCLK_ENABLE, 20238e2bab3fSAlgea Cao HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS); 20248e2bab3fSAlgea Cao 20258e2bab3fSAlgea Cao printf("%s success\n", __func__); 20268e2bab3fSAlgea Cao } 20278e2bab3fSAlgea Cao 2028f5e7d251SAlgea Cao static int dw_hdmi_setup(struct dw_hdmi *hdmi, 20298e2bab3fSAlgea Cao struct drm_display_mode *mode, 20308e2bab3fSAlgea Cao struct display_state *state) 2031f5e7d251SAlgea Cao { 2032f5e7d251SAlgea Cao int ret; 2033f5e7d251SAlgea Cao void *data = hdmi->plat_data->phy_data; 2034f5e7d251SAlgea Cao 2035f5e7d251SAlgea Cao hdmi_disable_overflow_interrupts(hdmi); 2036f5e7d251SAlgea Cao if (!hdmi->vic) 2037f5e7d251SAlgea Cao printf("Non-CEA mode used in HDMI\n"); 2038f5e7d251SAlgea Cao else 2039f5e7d251SAlgea Cao printf("CEA mode used vic=%d\n", hdmi->vic); 2040f5e7d251SAlgea Cao 2041f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_out_encoding) 2042f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = 2043f5e7d251SAlgea Cao hdmi->plat_data->get_enc_out_encoding(data); 20445ccad8f6SAlgea Cao else if (hdmi->vic == 6 || hdmi->vic == 7 || 20455ccad8f6SAlgea Cao hdmi->vic == 21 || hdmi->vic == 22 || 20465ccad8f6SAlgea Cao hdmi->vic == 2 || hdmi->vic == 3 || 20475ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18) 2048f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; 2049f5e7d251SAlgea Cao else 2050f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; 2051f5e7d251SAlgea Cao 2052f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_DBLCLK) { 2053f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; 2054f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; 2055f5e7d251SAlgea Cao } else { 2056f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; 2057f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; 2058f5e7d251SAlgea Cao } 2059f5e7d251SAlgea Cao 2060f5e7d251SAlgea Cao /* TOFIX: Get input encoding from plat data or fallback to none */ 2061f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_in_encoding) 2062f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = 2063f5e7d251SAlgea Cao hdmi->plat_data->get_enc_in_encoding(data); 2064f5e7d251SAlgea Cao else if (hdmi->plat_data->input_bus_encoding) 2065f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = 2066f5e7d251SAlgea Cao hdmi->plat_data->input_bus_encoding; 2067f5e7d251SAlgea Cao else 2068f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; 2069b5016cf2SAlgea Cao 2070b5016cf2SAlgea Cao if (hdmi->plat_data->get_quant_range) 2071b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range = 2072b5016cf2SAlgea Cao hdmi->plat_data->get_quant_range(data); 2073b5016cf2SAlgea Cao else 2074b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; 2075b5016cf2SAlgea Cao 2076f5e7d251SAlgea Cao /* 2077f5e7d251SAlgea Cao * According to the dw-hdmi specification 6.4.2 2078f5e7d251SAlgea Cao * vp_pr_cd[3:0]: 2079f5e7d251SAlgea Cao * 0000b: No pixel repetition (pixel sent only once) 2080f5e7d251SAlgea Cao * 0001b: Pixel sent two times (pixel repeated once) 2081f5e7d251SAlgea Cao */ 2082f5e7d251SAlgea Cao hdmi->hdmi_data.pix_repet_factor = 2083f5e7d251SAlgea Cao (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; 2084f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mdataenablepolarity = true; 2085f5e7d251SAlgea Cao 2086f5e7d251SAlgea Cao /* HDMI Initialization Step B.1 */ 2087f5e7d251SAlgea Cao hdmi_av_composer(hdmi, mode); 2088f5e7d251SAlgea Cao 2089f5e7d251SAlgea Cao /* HDMI Initialization Step B.2 */ 20908e2bab3fSAlgea Cao ret = hdmi->phy.ops->init(hdmi, state); 2091f5e7d251SAlgea Cao if (ret) 2092f5e7d251SAlgea Cao return ret; 2093f5e7d251SAlgea Cao hdmi->phy.enabled = true; 2094f5e7d251SAlgea Cao 2095f5e7d251SAlgea Cao /* HDMI Initializateion Step B.3 */ 2096f5e7d251SAlgea Cao dw_hdmi_enable_video_path(hdmi); 2097f5e7d251SAlgea Cao 2098f5e7d251SAlgea Cao /* HDMI Initialization Step E - Configure audio */ 2099f5e7d251SAlgea Cao if (hdmi->sink_has_audio) { 2100f5e7d251SAlgea Cao printf("sink has audio support\n"); 2101f5e7d251SAlgea Cao hdmi_clk_regenerator_update_pixel_clock(hdmi); 2102f5e7d251SAlgea Cao hdmi_enable_audio_clk(hdmi); 2103f5e7d251SAlgea Cao } 2104f5e7d251SAlgea Cao 2105f5e7d251SAlgea Cao /* not for DVI mode */ 2106f5e7d251SAlgea Cao if (hdmi->sink_is_hdmi) { 2107f5e7d251SAlgea Cao /* HDMI Initialization Step F - Configure AVI InfoFrame */ 2108f5e7d251SAlgea Cao hdmi_config_AVI(hdmi, mode); 2109f5e7d251SAlgea Cao hdmi_config_vendor_specific_infoframe(hdmi, mode); 2110b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_HDMI, 2111b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK, 2112b327b539SShunqing Chen HDMI_A_HDCPCFG0); 2113f5e7d251SAlgea Cao } else { 2114b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_DVI, 2115b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK, 2116b327b539SShunqing Chen HDMI_A_HDCPCFG0); 2117f5e7d251SAlgea Cao printf("%s DVI mode\n", __func__); 2118f5e7d251SAlgea Cao } 2119f5e7d251SAlgea Cao 2120f5e7d251SAlgea Cao hdmi_video_packetize(hdmi); 2121f5e7d251SAlgea Cao hdmi_video_csc(hdmi); 2122f5e7d251SAlgea Cao hdmi_video_sample(hdmi); 21238e2bab3fSAlgea Cao hdmi_tx_hdcp_config(hdmi, mode); 2124f5e7d251SAlgea Cao dw_hdmi_clear_overflow(hdmi); 2125f5e7d251SAlgea Cao 2126f5e7d251SAlgea Cao return 0; 2127f5e7d251SAlgea Cao } 2128f5e7d251SAlgea Cao 21298e2bab3fSAlgea Cao int dw_hdmi_detect_hotplug(struct dw_hdmi *hdmi, 21308e2bab3fSAlgea Cao struct display_state *state) 2131f5e7d251SAlgea Cao { 21328e2bab3fSAlgea Cao return hdmi->phy.ops->read_hpd(hdmi, state); 2133f5e7d251SAlgea Cao } 2134f5e7d251SAlgea Cao 2135f5e7d251SAlgea Cao static int dw_hdmi_set_reg_wr(struct dw_hdmi *hdmi) 2136f5e7d251SAlgea Cao { 2137f5e7d251SAlgea Cao switch (hdmi->io_width) { 2138f5e7d251SAlgea Cao case 4: 2139f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writel; 2140f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readl; 2141f5e7d251SAlgea Cao break; 2142f5e7d251SAlgea Cao case 1: 2143f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writeb; 2144f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readb; 2145f5e7d251SAlgea Cao break; 2146f5e7d251SAlgea Cao default: 2147f5e7d251SAlgea Cao printf("reg-io-width must be 1 or 4\n"); 2148f5e7d251SAlgea Cao return -EINVAL; 2149f5e7d251SAlgea Cao } 2150f5e7d251SAlgea Cao 2151f5e7d251SAlgea Cao return 0; 2152f5e7d251SAlgea Cao } 2153f5e7d251SAlgea Cao 2154f5e7d251SAlgea Cao static void initialize_hdmi_mutes(struct dw_hdmi *hdmi) 2155f5e7d251SAlgea Cao { 2156f5e7d251SAlgea Cao /*mute unnecessary interrupt, only enable hpd */ 21575ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0); 21585ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1); 21595ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2); 2160f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0); 2161f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1); 2162f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2); 2163f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0); 2164f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xfe, HDMI_IH_MUTE_PHY_STAT0); 2165f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0); 2166f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0); 2167f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0); 2168f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0); 2169f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0); 2170f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xf1, HDMI_PHY_MASK0); 2171f5e7d251SAlgea Cao 2172f5e7d251SAlgea Cao /*Force output black*/ 2173f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS2); 2174f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS1); 2175f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS0); 2176f5e7d251SAlgea Cao } 2177f5e7d251SAlgea Cao 2178f5e7d251SAlgea Cao static void dw_hdmi_dev_init(struct dw_hdmi *hdmi) 2179f5e7d251SAlgea Cao { 2180f5e7d251SAlgea Cao hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) 2181f5e7d251SAlgea Cao | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); 2182f5e7d251SAlgea Cao 2183f5e7d251SAlgea Cao initialize_hdmi_mutes(hdmi); 2184f5e7d251SAlgea Cao } 2185f5e7d251SAlgea Cao 21868e2bab3fSAlgea Cao static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi) 2187f5e7d251SAlgea Cao { 21888e2bab3fSAlgea Cao unsigned long low_ns, high_ns; 21898e2bab3fSAlgea Cao unsigned long div_low, div_high; 2190f5e7d251SAlgea Cao 21918e2bab3fSAlgea Cao /* Standard-mode */ 21928e2bab3fSAlgea Cao if (hdmi->i2c->scl_high_ns < 4000) 21938e2bab3fSAlgea Cao high_ns = 4708; 2194f5e7d251SAlgea Cao else 21958e2bab3fSAlgea Cao high_ns = hdmi->i2c->scl_high_ns; 2196f5e7d251SAlgea Cao 21978e2bab3fSAlgea Cao if (hdmi->i2c->scl_low_ns < 4700) 21988e2bab3fSAlgea Cao low_ns = 4916; 21998e2bab3fSAlgea Cao else 22008e2bab3fSAlgea Cao low_ns = hdmi->i2c->scl_low_ns; 22018e2bab3fSAlgea Cao 22028e2bab3fSAlgea Cao div_low = (24000 * low_ns) / 1000000; 22038e2bab3fSAlgea Cao if ((24000 * low_ns) % 1000000) 22048e2bab3fSAlgea Cao div_low++; 22058e2bab3fSAlgea Cao 22068e2bab3fSAlgea Cao div_high = (24000 * high_ns) / 1000000; 22078e2bab3fSAlgea Cao if ((24000 * high_ns) % 1000000) 22088e2bab3fSAlgea Cao div_high++; 22098e2bab3fSAlgea Cao 22108e2bab3fSAlgea Cao /* Maximum divider supported by hw is 0xffff */ 22118e2bab3fSAlgea Cao if (div_low > 0xffff) 22128e2bab3fSAlgea Cao div_low = 0xffff; 22138e2bab3fSAlgea Cao 22148e2bab3fSAlgea Cao if (div_high > 0xffff) 22158e2bab3fSAlgea Cao div_high = 0xffff; 22168e2bab3fSAlgea Cao 22178e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR); 22188e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_high >> 8) & 0xff, 22198e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_HCNT_1_ADDR); 22208e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR); 22218e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_low >> 8) & 0xff, 22228e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_LCNT_1_ADDR); 2223f5e7d251SAlgea Cao } 2224f5e7d251SAlgea Cao 22258e2bab3fSAlgea Cao static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) 2226f5e7d251SAlgea Cao { 22278e2bab3fSAlgea Cao /* Software reset */ 22288e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); 2229f5e7d251SAlgea Cao 22308e2bab3fSAlgea Cao /* Set Standard Mode speed */ 22318e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE, 22328e2bab3fSAlgea Cao HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV); 2233f5e7d251SAlgea Cao 22348e2bab3fSAlgea Cao /* Set done, not acknowledged and arbitration interrupt polarities */ 22358e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT); 22368e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL, 22378e2bab3fSAlgea Cao HDMI_I2CM_CTLINT); 2238f5e7d251SAlgea Cao 22398e2bab3fSAlgea Cao /* Clear DONE and ERROR interrupts */ 22408e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 22418e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0); 2242f5e7d251SAlgea Cao 22438e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */ 22448e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 22458e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0); 2246f5e7d251SAlgea Cao 22478e2bab3fSAlgea Cao /* set SDA high level holding time */ 22488e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD); 2249f5e7d251SAlgea Cao 22508e2bab3fSAlgea Cao dw_hdmi_i2c_set_divs(hdmi); 2251f5e7d251SAlgea Cao } 2252f5e7d251SAlgea Cao 2253f5e7d251SAlgea Cao void dw_hdmi_audio_enable(struct dw_hdmi *hdmi) 2254f5e7d251SAlgea Cao { 2255f5e7d251SAlgea Cao hdmi->audio_enable = true; 2256f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); 2257f5e7d251SAlgea Cao } 2258f5e7d251SAlgea Cao 2259f5e7d251SAlgea Cao void dw_hdmi_audio_disable(struct dw_hdmi *hdmi) 2260f5e7d251SAlgea Cao { 2261f5e7d251SAlgea Cao hdmi->audio_enable = false; 2262f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); 2263f5e7d251SAlgea Cao } 2264f5e7d251SAlgea Cao 226558c17f51SSandy Huang int rockchip_dw_hdmi_pre_init(struct display_state *state) 226658c17f51SSandy Huang { 226758c17f51SSandy Huang struct connector_state *conn_state = &state->conn_state; 226858c17f51SSandy Huang 226958c17f51SSandy Huang conn_state->type = DRM_MODE_CONNECTOR_HDMIA; 227058c17f51SSandy Huang 227158c17f51SSandy Huang return 0; 227258c17f51SSandy Huang } 227358c17f51SSandy Huang 2274f5e7d251SAlgea Cao int rockchip_dw_hdmi_init(struct display_state *state) 2275f5e7d251SAlgea Cao { 2276f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2277f5e7d251SAlgea Cao const struct rockchip_connector *connector = conn_state->connector; 2278f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata = connector->data; 2279f5e7d251SAlgea Cao struct crtc_state *crtc_state = &state->crtc_state; 2280f5e7d251SAlgea Cao struct dw_hdmi *hdmi; 2281f5e7d251SAlgea Cao struct drm_display_mode *mode_buf; 2282e2bce6e4SKever Yang ofnode hdmi_node = conn_state->node; 2283f5e7d251SAlgea Cao u32 val; 22841f71919fSShunqing Chen struct device_node *ddc_node; 2285*cb17ca6cSSandy Huang int id; 2286f5e7d251SAlgea Cao 2287f5e7d251SAlgea Cao hdmi = malloc(sizeof(struct dw_hdmi)); 2288f5e7d251SAlgea Cao if (!hdmi) 2289f5e7d251SAlgea Cao return -ENOMEM; 2290*cb17ca6cSSandy Huang id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi"); 2291*cb17ca6cSSandy Huang if (id < 0) 2292*cb17ca6cSSandy Huang id = 0; 2293*cb17ca6cSSandy Huang hdmi->id = id; 2294*cb17ca6cSSandy Huang conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id); 2295f5e7d251SAlgea Cao 2296f5e7d251SAlgea Cao memset(hdmi, 0, sizeof(struct dw_hdmi)); 2297f5e7d251SAlgea Cao mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode)); 2298f5e7d251SAlgea Cao if (!mode_buf) 2299f5e7d251SAlgea Cao return -ENOMEM; 2300*cb17ca6cSSandy Huang hdmi->id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi"); 2301*cb17ca6cSSandy Huang if (hdmi->id < 0) 2302*cb17ca6cSSandy Huang hdmi->id = 0; 2303*cb17ca6cSSandy Huang conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id); 23048e2bab3fSAlgea Cao 2305f5e7d251SAlgea Cao memset(mode_buf, 0, MODE_LEN * sizeof(struct drm_display_mode)); 2306f5e7d251SAlgea Cao 2307e2bce6e4SKever Yang hdmi->regs = dev_read_addr_ptr(conn_state->dev); 2308e2bce6e4SKever Yang hdmi->io_width = ofnode_read_s32_default(hdmi_node, "reg-io-width", -1); 23098e2bab3fSAlgea Cao 23108e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "scramble-low-rates")) 23118e2bab3fSAlgea Cao hdmi->scramble_low_rates = true; 23128e2bab3fSAlgea Cao 23138e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "hdcp1x-enable")) 23148e2bab3fSAlgea Cao hdmi->hdcp1x_enable = true; 23158e2bab3fSAlgea Cao else 23168e2bab3fSAlgea Cao hdmi->hdcp1x_enable = false; 23178e2bab3fSAlgea Cao 23181f71919fSShunqing Chen ddc_node = of_parse_phandle(ofnode_to_np(hdmi_node), "ddc-i2c-bus", 0); 23191f71919fSShunqing Chen if (ddc_node) { 23201f71919fSShunqing Chen uclass_get_device_by_ofnode(UCLASS_I2C, np_to_ofnode(ddc_node), 23211f71919fSShunqing Chen &hdmi->adap.i2c_bus); 23221f71919fSShunqing Chen if (hdmi->adap.i2c_bus) 23231f71919fSShunqing Chen hdmi->adap.ops = i2c_get_ops(hdmi->adap.i2c_bus); 23241f71919fSShunqing Chen } 23251f71919fSShunqing Chen 2326f5e7d251SAlgea Cao hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 2327f5e7d251SAlgea Cao if (hdmi->grf <= 0) { 2328f5e7d251SAlgea Cao printf("%s: Get syscon grf failed (ret=%p)\n", 2329f5e7d251SAlgea Cao __func__, hdmi->grf); 2330f5e7d251SAlgea Cao return -ENXIO; 2331f5e7d251SAlgea Cao } 2332f5e7d251SAlgea Cao 2333f5e7d251SAlgea Cao dw_hdmi_set_reg_wr(hdmi); 2334f5e7d251SAlgea Cao 23358e2bab3fSAlgea Cao if (pdata->grf_vop_sel_reg) { 2336f5e7d251SAlgea Cao if (crtc_state->crtc_id) 2337f5e7d251SAlgea Cao val = ((1 << pdata->vop_sel_bit) | 2338f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit))); 2339f5e7d251SAlgea Cao else 2340f5e7d251SAlgea Cao val = ((0 << pdata->vop_sel_bit) | 2341f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit))); 2342f5e7d251SAlgea Cao writel(val, hdmi->grf + pdata->grf_vop_sel_reg); 23438e2bab3fSAlgea Cao } 2344f5e7d251SAlgea Cao 23458e2bab3fSAlgea Cao hdmi->i2c = malloc(sizeof(struct dw_hdmi_i2c)); 23468e2bab3fSAlgea Cao if (!hdmi->i2c) 23478e2bab3fSAlgea Cao return -ENOMEM; 23488e2bab3fSAlgea Cao hdmi->adap.ddc_xfer = dw_hdmi_i2c_xfer; 23498e2bab3fSAlgea Cao 23508e2bab3fSAlgea Cao /* 23518e2bab3fSAlgea Cao * Read high and low time from device tree. If not available use 23528e2bab3fSAlgea Cao * the default timing scl clock rate is about 99.6KHz. 23538e2bab3fSAlgea Cao */ 23548e2bab3fSAlgea Cao hdmi->i2c->scl_high_ns = 23558e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node, 235619c2faf2SAlgea Cao "ddc-i2c-scl-high-time-ns", 4708); 23578e2bab3fSAlgea Cao hdmi->i2c->scl_low_ns = 23588e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node, 235919c2faf2SAlgea Cao "ddc-i2c-scl-low-time-ns", 4916); 23608e2bab3fSAlgea Cao 23618e2bab3fSAlgea Cao dw_hdmi_i2c_init(hdmi); 23625ccad8f6SAlgea Cao conn_state->output_if |= VOP_OUTPUT_IF_HDMI0; 2363f5e7d251SAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; 2364f5e7d251SAlgea Cao 2365f5e7d251SAlgea Cao hdmi->dev_type = pdata->dev_type; 2366f5e7d251SAlgea Cao hdmi->plat_data = pdata; 2367f5e7d251SAlgea Cao hdmi->edid_data.mode_buf = mode_buf; 2368f5e7d251SAlgea Cao hdmi->sample_rate = 48000; 2369f5e7d251SAlgea Cao 2370f5e7d251SAlgea Cao conn_state->private = hdmi; 23718e2bab3fSAlgea Cao dw_hdmi_set_iomux(hdmi->grf, hdmi->dev_type); 2372f5e7d251SAlgea Cao dw_hdmi_detect_phy(hdmi); 2373f5e7d251SAlgea Cao dw_hdmi_dev_init(hdmi); 2374f5e7d251SAlgea Cao 2375f5e7d251SAlgea Cao return 0; 2376f5e7d251SAlgea Cao } 2377f5e7d251SAlgea Cao 2378f5e7d251SAlgea Cao void rockchip_dw_hdmi_deinit(struct display_state *state) 2379f5e7d251SAlgea Cao { 2380f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2381f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 2382f5e7d251SAlgea Cao 23838e2bab3fSAlgea Cao if (hdmi->i2c) 23848e2bab3fSAlgea Cao free(hdmi->i2c); 2385f5e7d251SAlgea Cao if (hdmi->edid_data.mode_buf) 2386f5e7d251SAlgea Cao free(hdmi->edid_data.mode_buf); 2387f5e7d251SAlgea Cao if (hdmi) 2388f5e7d251SAlgea Cao free(hdmi); 2389f5e7d251SAlgea Cao } 2390f5e7d251SAlgea Cao 2391f5e7d251SAlgea Cao int rockchip_dw_hdmi_prepare(struct display_state *state) 2392f5e7d251SAlgea Cao { 2393f5e7d251SAlgea Cao return 0; 2394f5e7d251SAlgea Cao } 2395f5e7d251SAlgea Cao 2396f5e7d251SAlgea Cao int rockchip_dw_hdmi_enable(struct display_state *state) 2397f5e7d251SAlgea Cao { 2398f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2399f5e7d251SAlgea Cao struct drm_display_mode *mode = &conn_state->mode; 2400f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 2401f5e7d251SAlgea Cao 2402f5e7d251SAlgea Cao if (!hdmi) 2403f5e7d251SAlgea Cao return -EFAULT; 2404f5e7d251SAlgea Cao 24058e2bab3fSAlgea Cao /* Store the display mode for plugin/DKMS poweron events */ 24068e2bab3fSAlgea Cao memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); 24078e2bab3fSAlgea Cao 24088e2bab3fSAlgea Cao dw_hdmi_setup(hdmi, mode, state); 2409f5e7d251SAlgea Cao 2410f5e7d251SAlgea Cao return 0; 2411f5e7d251SAlgea Cao } 2412f5e7d251SAlgea Cao 2413f5e7d251SAlgea Cao int rockchip_dw_hdmi_disable(struct display_state *state) 2414f5e7d251SAlgea Cao { 2415f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2416f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 2417f5e7d251SAlgea Cao 24188e2bab3fSAlgea Cao dw_hdmi_disable(hdmi, state); 2419f5e7d251SAlgea Cao return 0; 2420f5e7d251SAlgea Cao } 2421f5e7d251SAlgea Cao 2422f5e7d251SAlgea Cao int rockchip_dw_hdmi_get_timing(struct display_state *state) 2423f5e7d251SAlgea Cao { 24248e2bab3fSAlgea Cao int ret, i; 2425f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2426f5e7d251SAlgea Cao struct drm_display_mode *mode = &conn_state->mode; 2427f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 24288e2bab3fSAlgea Cao struct edid *edid = (struct edid *)conn_state->edid; 24298e2bab3fSAlgea Cao unsigned int bus_format; 24305ccad8f6SAlgea Cao unsigned long enc_out_encoding; 24318e2bab3fSAlgea Cao struct overscan *overscan = &conn_state->overscan; 24328e2bab3fSAlgea Cao const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19}; 2433f5e7d251SAlgea Cao 2434f5e7d251SAlgea Cao if (!hdmi) 2435f5e7d251SAlgea Cao return -EFAULT; 2436f5e7d251SAlgea Cao 24378e2bab3fSAlgea Cao ret = drm_do_get_edid(&hdmi->adap, conn_state->edid); 24385ccad8f6SAlgea Cao 24398e2bab3fSAlgea Cao if (!ret) { 2440f5e7d251SAlgea Cao hdmi->sink_is_hdmi = 24418e2bab3fSAlgea Cao drm_detect_hdmi_monitor(edid); 24428e2bab3fSAlgea Cao hdmi->sink_has_audio = drm_detect_monitor_audio(edid); 24438e2bab3fSAlgea Cao ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid); 24448e2bab3fSAlgea Cao } 2445c613b7eaSAlgea Cao if (ret < 0) { 24468e2bab3fSAlgea Cao hdmi->sink_is_hdmi = true; 24478e2bab3fSAlgea Cao hdmi->sink_has_audio = true; 24488e2bab3fSAlgea Cao do_cea_modes(&hdmi->edid_data, def_modes_vic, 24498e2bab3fSAlgea Cao sizeof(def_modes_vic)); 24508e2bab3fSAlgea Cao hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0]; 24518e2bab3fSAlgea Cao printf("failed to get edid\n"); 24528e2bab3fSAlgea Cao } 24538e2bab3fSAlgea Cao drm_rk_filter_whitelist(&hdmi->edid_data); 24548e2bab3fSAlgea Cao if (hdmi->phy.ops->mode_valid) 24558e2bab3fSAlgea Cao hdmi->phy.ops->mode_valid(hdmi, state); 2456cf53642aSSandy Huang drm_mode_max_resolution_filter(&hdmi->edid_data, 2457cf53642aSSandy Huang &state->crtc_state.max_output); 24588e2bab3fSAlgea Cao if (!drm_mode_prune_invalid(&hdmi->edid_data)) { 24598e2bab3fSAlgea Cao printf("can't find valid hdmi mode\n"); 24608e2bab3fSAlgea Cao return -EINVAL; 24618e2bab3fSAlgea Cao } 24628e2bab3fSAlgea Cao 24638e2bab3fSAlgea Cao for (i = 0; i < hdmi->edid_data.modes; i++) 24648e2bab3fSAlgea Cao hdmi->edid_data.mode_buf[i].vrefresh = 24658e2bab3fSAlgea Cao drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]); 24668e2bab3fSAlgea Cao 24678e2bab3fSAlgea Cao drm_mode_sort(&hdmi->edid_data); 24688e2bab3fSAlgea Cao drm_rk_selete_output(&hdmi->edid_data, &bus_format, 24698e2bab3fSAlgea Cao overscan, hdmi->dev_type); 24708e2bab3fSAlgea Cao 2471f5e7d251SAlgea Cao *mode = *hdmi->edid_data.preferred_mode; 2472f5e7d251SAlgea Cao hdmi->vic = drm_match_cea_mode(mode); 2473f5e7d251SAlgea Cao 24748e2bab3fSAlgea Cao printf("mode:%dx%d\n", mode->hdisplay, mode->vdisplay); 24758e2bab3fSAlgea Cao conn_state->bus_format = bus_format; 24768e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = bus_format; 24778e2bab3fSAlgea Cao hdmi->hdmi_data.enc_out_bus_format = bus_format; 2478f5e7d251SAlgea Cao 24798e2bab3fSAlgea Cao switch (bus_format) { 24808e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 24818e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV10_1X30; 24828e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = 24838e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV10_1X30; 24848e2bab3fSAlgea Cao break; 24858e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 24868e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV8_1X24; 24878e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = 24888e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV8_1X24; 24898e2bab3fSAlgea Cao break; 24908e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 24918e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 24928e2bab3fSAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV420; 24938e2bab3fSAlgea Cao break; 24948e2bab3fSAlgea Cao } 2495f5e7d251SAlgea Cao 24965ccad8f6SAlgea Cao if (hdmi->vic == 6 || hdmi->vic == 7 || hdmi->vic == 21 || 24975ccad8f6SAlgea Cao hdmi->vic == 22 || hdmi->vic == 2 || hdmi->vic == 3 || 24985ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18) 24995ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_601; 25005ccad8f6SAlgea Cao else 25015ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_709; 25025ccad8f6SAlgea Cao 25035ccad8f6SAlgea Cao if (enc_out_encoding == V4L2_YCBCR_ENC_BT2020) 25045ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_BT2020; 25055ccad8f6SAlgea Cao else if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 || 25065ccad8f6SAlgea Cao bus_format == MEDIA_BUS_FMT_RGB101010_1X30) 25075ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_DEFAULT; 25085ccad8f6SAlgea Cao else if (enc_out_encoding == V4L2_YCBCR_ENC_709) 25095ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_REC709; 25105ccad8f6SAlgea Cao else 25115ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_SMPTE170M; 25125ccad8f6SAlgea Cao 2513f5e7d251SAlgea Cao return 0; 2514f5e7d251SAlgea Cao } 2515f5e7d251SAlgea Cao 2516f5e7d251SAlgea Cao int rockchip_dw_hdmi_detect(struct display_state *state) 2517f5e7d251SAlgea Cao { 2518f5e7d251SAlgea Cao int ret; 2519f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2520f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 2521f5e7d251SAlgea Cao 2522f5e7d251SAlgea Cao if (!hdmi) 2523f5e7d251SAlgea Cao return -EFAULT; 2524f5e7d251SAlgea Cao 25258e2bab3fSAlgea Cao ret = dw_hdmi_detect_hotplug(hdmi, state); 2526f5e7d251SAlgea Cao 2527f5e7d251SAlgea Cao return ret; 2528f5e7d251SAlgea Cao } 2529f5e7d251SAlgea Cao 2530f5e7d251SAlgea Cao int rockchip_dw_hdmi_get_edid(struct display_state *state) 2531f5e7d251SAlgea Cao { 2532f5e7d251SAlgea Cao int ret; 2533f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2534f5e7d251SAlgea Cao struct dw_hdmi *hdmi = conn_state->private; 2535f5e7d251SAlgea Cao 25368e2bab3fSAlgea Cao ret = drm_do_get_edid(&hdmi->adap, conn_state->edid); 2537f5e7d251SAlgea Cao 2538f5e7d251SAlgea Cao return ret; 2539f5e7d251SAlgea Cao } 2540f5e7d251SAlgea Cao 25418e2bab3fSAlgea Cao int inno_dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data) 25428e2bab3fSAlgea Cao { 25438e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data; 25448e2bab3fSAlgea Cao struct connector_state *conn_state = &state->conn_state; 25458e2bab3fSAlgea Cao u32 color_depth, bus_width; 25468e2bab3fSAlgea Cao 25478e2bab3fSAlgea Cao color_depth = 25488e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 25498e2bab3fSAlgea Cao 25508e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 25518e2bab3fSAlgea Cao bus_width = color_depth / 2; 25528e2bab3fSAlgea Cao else if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 25538e2bab3fSAlgea Cao bus_width = color_depth; 25548e2bab3fSAlgea Cao else 25558e2bab3fSAlgea Cao bus_width = 8; 255615081c50SWyon Bi rockchip_phy_set_bus_width(conn_state->phy, bus_width); 255715081c50SWyon Bi rockchip_phy_set_pll(conn_state->phy, 255815081c50SWyon Bi conn_state->mode.crtc_clock * 1000); 25598e2bab3fSAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported) 25608e2bab3fSAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi); 256115081c50SWyon Bi rockchip_phy_power_on(conn_state->phy); 25628e2bab3fSAlgea Cao 25638e2bab3fSAlgea Cao return 0; 25648e2bab3fSAlgea Cao } 25658e2bab3fSAlgea Cao 25668e2bab3fSAlgea Cao void inno_dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) 25678e2bab3fSAlgea Cao { 25688e2bab3fSAlgea Cao } 25698e2bab3fSAlgea Cao 25708e2bab3fSAlgea Cao enum drm_connector_status 25718e2bab3fSAlgea Cao inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data) 25728e2bab3fSAlgea Cao { 25738e2bab3fSAlgea Cao enum drm_connector_status status; 25748e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data; 25758e2bab3fSAlgea Cao 25768e2bab3fSAlgea Cao status = dw_hdmi_phy_read_hpd(hdmi, state); 25778e2bab3fSAlgea Cao 25788e2bab3fSAlgea Cao if (hdmi->dev_type == RK3328_HDMI) { 25798e2bab3fSAlgea Cao if (status == connector_status_connected) 25808e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 1); 25818e2bab3fSAlgea Cao else 25828e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 0); 25838e2bab3fSAlgea Cao } 25848e2bab3fSAlgea Cao 25858e2bab3fSAlgea Cao return status; 25868e2bab3fSAlgea Cao } 25878e2bab3fSAlgea Cao 25888e2bab3fSAlgea Cao void inno_dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data) 25898e2bab3fSAlgea Cao { 25908e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data; 259115081c50SWyon Bi struct connector_state *conn_state = &state->conn_state; 25928e2bab3fSAlgea Cao struct hdmi_edid_data *edid_data = &hdmi->edid_data; 25938e2bab3fSAlgea Cao unsigned long rate; 25948e2bab3fSAlgea Cao int i, ret; 25958e2bab3fSAlgea Cao struct drm_display_mode *mode_buf = edid_data->mode_buf; 25968e2bab3fSAlgea Cao 25978e2bab3fSAlgea Cao for (i = 0; i < edid_data->modes; i++) { 25988e2bab3fSAlgea Cao if (edid_data->mode_buf[i].invalid) 25998e2bab3fSAlgea Cao continue; 26008e2bab3fSAlgea Cao if (edid_data->mode_buf[i].flags & DRM_MODE_FLAG_DBLCLK) 26018e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000 * 2; 26028e2bab3fSAlgea Cao else 26038e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000; 26048e2bab3fSAlgea Cao 26058e2bab3fSAlgea Cao /* Check whether mode is out of phy cfg range. */ 260615081c50SWyon Bi ret = rockchip_phy_round_rate(conn_state->phy, rate); 26078e2bab3fSAlgea Cao 26088e2bab3fSAlgea Cao if (ret < 0) 26098e2bab3fSAlgea Cao edid_data->mode_buf[i].invalid = true; 26108e2bab3fSAlgea Cao } 26118e2bab3fSAlgea Cao } 2612