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> 10cb24dc0eSAlgea Cao #include <asm/gpio.h> 11f5e7d251SAlgea Cao #include <asm/arch-rockchip/clock.h> 128e2bab3fSAlgea Cao #include <asm/arch/vendor.h> 13f5e7d251SAlgea Cao #include <edid.h> 14e2bce6e4SKever Yang #include <dm/device.h> 151f71919fSShunqing Chen #include <dm/of_access.h> 168e2bab3fSAlgea Cao #include <dm/ofnode.h> 17e2bce6e4SKever Yang #include <dm/read.h> 18f5e7d251SAlgea Cao #include <linux/hdmi.h> 19f5e7d251SAlgea Cao #include <linux/media-bus-format.h> 20f5e7d251SAlgea Cao #include <linux/dw_hdmi.h> 21f5e7d251SAlgea Cao #include <asm/io.h> 22f5e7d251SAlgea Cao #include "rockchip_display.h" 23f5e7d251SAlgea Cao #include "rockchip_crtc.h" 24f5e7d251SAlgea Cao #include "rockchip_connector.h" 25f5e7d251SAlgea Cao #include "dw_hdmi.h" 268e2bab3fSAlgea Cao #include "rockchip_phy.h" 27f5e7d251SAlgea Cao 288e2bab3fSAlgea Cao #define HDCP_PRIVATE_KEY_SIZE 280 298e2bab3fSAlgea Cao #define HDCP_KEY_SHA_SIZE 20 308e2bab3fSAlgea Cao #define HDMI_HDCP1X_ID 5 31*d9939fc9SAlgea Cao #define HDMI_EDID_BLOCK_LEN 128 32f5e7d251SAlgea Cao /* 33f5e7d251SAlgea Cao * Unless otherwise noted, entries in this table are 100% optimization. 34f5e7d251SAlgea Cao * Values can be obtained from hdmi_compute_n() but that function is 35f5e7d251SAlgea Cao * slow so we pre-compute values we expect to see. 36f5e7d251SAlgea Cao * 37f5e7d251SAlgea Cao * All 32k and 48k values are expected to be the same (due to the way 38f5e7d251SAlgea Cao * the math works) for any rate that's an exact kHz. 39f5e7d251SAlgea Cao */ 40f5e7d251SAlgea Cao static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { 41f5e7d251SAlgea Cao { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, }, 42f5e7d251SAlgea Cao { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, }, 43f5e7d251SAlgea Cao { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 44f5e7d251SAlgea Cao { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, 45f5e7d251SAlgea Cao { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, 46f5e7d251SAlgea Cao { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 47f5e7d251SAlgea Cao { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 48f5e7d251SAlgea Cao { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 49f5e7d251SAlgea Cao { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 50f5e7d251SAlgea Cao { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, 51f5e7d251SAlgea Cao { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 52f5e7d251SAlgea Cao { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 53f5e7d251SAlgea Cao { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 54f5e7d251SAlgea Cao { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 55f5e7d251SAlgea Cao { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, 56f5e7d251SAlgea Cao { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 57f5e7d251SAlgea Cao { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, 58f5e7d251SAlgea Cao { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 59f5e7d251SAlgea Cao { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 60f5e7d251SAlgea Cao { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, 61f5e7d251SAlgea Cao { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, 62f5e7d251SAlgea Cao { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, 63f5e7d251SAlgea Cao { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 64f5e7d251SAlgea Cao { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 65f5e7d251SAlgea Cao { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 66f5e7d251SAlgea Cao { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 67f5e7d251SAlgea Cao { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, 68f5e7d251SAlgea Cao { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, 69f5e7d251SAlgea Cao { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, 70f5e7d251SAlgea Cao { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 71f5e7d251SAlgea Cao { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, 72f5e7d251SAlgea Cao { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 73f5e7d251SAlgea Cao { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 74f5e7d251SAlgea Cao { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, 75f5e7d251SAlgea Cao { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, 76f5e7d251SAlgea Cao { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, 77f5e7d251SAlgea Cao { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, 78f5e7d251SAlgea Cao 79f5e7d251SAlgea Cao /* For 297 MHz+ HDMI spec have some other rule for setting N */ 80f5e7d251SAlgea Cao { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, 81f5e7d251SAlgea Cao { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, 82f5e7d251SAlgea Cao 83f5e7d251SAlgea Cao /* End of table */ 84f5e7d251SAlgea Cao { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, 85f5e7d251SAlgea Cao }; 86f5e7d251SAlgea Cao 87f5e7d251SAlgea Cao static const u16 csc_coeff_default[3][4] = { 88f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x0000, 0x0000 }, 89f5e7d251SAlgea Cao { 0x0000, 0x2000, 0x0000, 0x0000 }, 90f5e7d251SAlgea Cao { 0x0000, 0x0000, 0x2000, 0x0000 } 91f5e7d251SAlgea Cao }; 92f5e7d251SAlgea Cao 93f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu601[3][4] = { 94f5e7d251SAlgea Cao { 0x2000, 0x6926, 0x74fd, 0x010e }, 95f5e7d251SAlgea Cao { 0x2000, 0x2cdd, 0x0000, 0x7e9a }, 96f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x38b4, 0x7e3b } 97f5e7d251SAlgea Cao }; 98f5e7d251SAlgea Cao 99f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu709[3][4] = { 100f5e7d251SAlgea Cao { 0x2000, 0x7106, 0x7a02, 0x00a7 }, 101f5e7d251SAlgea Cao { 0x2000, 0x3264, 0x0000, 0x7e6d }, 102f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x3b61, 0x7e25 } 103f5e7d251SAlgea Cao }; 104f5e7d251SAlgea Cao 105f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu601[3][4] = { 106f5e7d251SAlgea Cao { 0x2591, 0x1322, 0x074b, 0x0000 }, 107f5e7d251SAlgea Cao { 0x6535, 0x2000, 0x7acc, 0x0200 }, 108f5e7d251SAlgea Cao { 0x6acd, 0x7534, 0x2000, 0x0200 } 109f5e7d251SAlgea Cao }; 110f5e7d251SAlgea Cao 111f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu709[3][4] = { 112f5e7d251SAlgea Cao { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, 113f5e7d251SAlgea Cao { 0x62f0, 0x2000, 0x7d11, 0x0200 }, 114f5e7d251SAlgea Cao { 0x6756, 0x78ab, 0x2000, 0x0200 } 115f5e7d251SAlgea Cao }; 116f5e7d251SAlgea Cao 117b5016cf2SAlgea Cao static const u16 csc_coeff_full_to_limited[3][4] = { 118b5016cf2SAlgea Cao { 0x36f7, 0x0000, 0x0000, 0x0040 }, 119b5016cf2SAlgea Cao { 0x0000, 0x36f7, 0x0000, 0x0040 }, 120b5016cf2SAlgea Cao { 0x0000, 0x0000, 0x36f7, 0x0040 } 121b5016cf2SAlgea Cao }; 122b5016cf2SAlgea Cao 123f5e7d251SAlgea Cao struct hdmi_vmode { 124f5e7d251SAlgea Cao bool mdataenablepolarity; 125f5e7d251SAlgea Cao 126f5e7d251SAlgea Cao unsigned int mpixelclock; 127f5e7d251SAlgea Cao unsigned int mpixelrepetitioninput; 128f5e7d251SAlgea Cao unsigned int mpixelrepetitionoutput; 1298e2bab3fSAlgea Cao unsigned int mtmdsclock; 130f5e7d251SAlgea Cao }; 131f5e7d251SAlgea Cao 132f5e7d251SAlgea Cao struct hdmi_data_info { 133f5e7d251SAlgea Cao unsigned int enc_in_bus_format; 134f5e7d251SAlgea Cao unsigned int enc_out_bus_format; 135f5e7d251SAlgea Cao unsigned int enc_in_encoding; 136f5e7d251SAlgea Cao unsigned int enc_out_encoding; 137b5016cf2SAlgea Cao unsigned int quant_range; 138f5e7d251SAlgea Cao unsigned int pix_repet_factor; 139f5e7d251SAlgea Cao struct hdmi_vmode video_mode; 140f5e7d251SAlgea Cao }; 141f5e7d251SAlgea Cao 142f5e7d251SAlgea Cao struct dw_hdmi_phy_data { 143f5e7d251SAlgea Cao enum dw_hdmi_phy_type type; 144f5e7d251SAlgea Cao const char *name; 145f5e7d251SAlgea Cao unsigned int gen; 146f5e7d251SAlgea Cao bool has_svsret; 147f5e7d251SAlgea Cao int (*configure)(struct dw_hdmi *hdmi, 148f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata, 149f5e7d251SAlgea Cao unsigned long mpixelclock); 150f5e7d251SAlgea Cao }; 151f5e7d251SAlgea Cao 1528e2bab3fSAlgea Cao struct hdcp_keys { 1538e2bab3fSAlgea Cao u8 KSV[8]; 1548e2bab3fSAlgea Cao u8 devicekey[HDCP_PRIVATE_KEY_SIZE]; 1558e2bab3fSAlgea Cao u8 sha1[HDCP_KEY_SHA_SIZE]; 1568e2bab3fSAlgea Cao u8 seeds[2]; 1578e2bab3fSAlgea Cao }; 1588e2bab3fSAlgea Cao 1598e2bab3fSAlgea Cao struct dw_hdmi_i2c { 1608e2bab3fSAlgea Cao u8 slave_reg; 1618e2bab3fSAlgea Cao bool is_regaddr; 1628e2bab3fSAlgea Cao bool is_segment; 1638e2bab3fSAlgea Cao 1648e2bab3fSAlgea Cao unsigned int scl_high_ns; 1658e2bab3fSAlgea Cao unsigned int scl_low_ns; 1668e2bab3fSAlgea Cao }; 1678e2bab3fSAlgea Cao 168f5e7d251SAlgea Cao struct dw_hdmi { 169cb17ca6cSSandy Huang int id; 170f5e7d251SAlgea Cao enum dw_hdmi_devtype dev_type; 171f5e7d251SAlgea Cao unsigned int version; 172f5e7d251SAlgea Cao struct hdmi_data_info hdmi_data; 173f5e7d251SAlgea Cao struct hdmi_edid_data edid_data; 174f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data; 1758e2bab3fSAlgea Cao struct ddc_adapter adap; 176f5e7d251SAlgea Cao 177f5e7d251SAlgea Cao int vic; 178f5e7d251SAlgea Cao int io_width; 179f5e7d251SAlgea Cao 180f5e7d251SAlgea Cao unsigned long bus_format; 181f5e7d251SAlgea Cao bool cable_plugin; 182f5e7d251SAlgea Cao bool sink_is_hdmi; 183f5e7d251SAlgea Cao bool sink_has_audio; 184f5e7d251SAlgea Cao void *regs; 185f5e7d251SAlgea Cao void *grf; 186cb24dc0eSAlgea Cao void *gpio_base; 1878e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c; 188f5e7d251SAlgea Cao 189f5e7d251SAlgea Cao struct { 190f5e7d251SAlgea Cao const struct dw_hdmi_phy_ops *ops; 191f5e7d251SAlgea Cao const char *name; 192f5e7d251SAlgea Cao void *data; 193f5e7d251SAlgea Cao bool enabled; 194f5e7d251SAlgea Cao } phy; 195f5e7d251SAlgea Cao 196f5e7d251SAlgea Cao struct drm_display_mode previous_mode; 197f5e7d251SAlgea Cao 198f5e7d251SAlgea Cao unsigned int sample_rate; 199f5e7d251SAlgea Cao unsigned int audio_cts; 200f5e7d251SAlgea Cao unsigned int audio_n; 201f5e7d251SAlgea Cao bool audio_enable; 2028e2bab3fSAlgea Cao bool scramble_low_rates; 203f5e7d251SAlgea Cao 204f5e7d251SAlgea Cao void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); 205f5e7d251SAlgea Cao u8 (*read)(struct dw_hdmi *hdmi, int offset); 2068e2bab3fSAlgea Cao 2078e2bab3fSAlgea Cao bool hdcp1x_enable; 20891e56900SLei Chen bool output_bus_format_rgb; 209cb24dc0eSAlgea Cao 210cb24dc0eSAlgea Cao struct gpio_desc hpd_gpiod; 211f5e7d251SAlgea Cao }; 212f5e7d251SAlgea Cao 213f5e7d251SAlgea Cao static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset) 214f5e7d251SAlgea Cao { 215f5e7d251SAlgea Cao writel(val, hdmi->regs + (offset << 2)); 216f5e7d251SAlgea Cao } 217f5e7d251SAlgea Cao 218f5e7d251SAlgea Cao static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset) 219f5e7d251SAlgea Cao { 220f5e7d251SAlgea Cao return readl(hdmi->regs + (offset << 2)); 221f5e7d251SAlgea Cao } 222f5e7d251SAlgea Cao 223f5e7d251SAlgea Cao static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) 224f5e7d251SAlgea Cao { 225f5e7d251SAlgea Cao writeb(val, hdmi->regs + offset); 226f5e7d251SAlgea Cao } 227f5e7d251SAlgea Cao 228f5e7d251SAlgea Cao static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset) 229f5e7d251SAlgea Cao { 230f5e7d251SAlgea Cao return readb(hdmi->regs + offset); 231f5e7d251SAlgea Cao } 232f5e7d251SAlgea Cao 233f5e7d251SAlgea Cao static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) 234f5e7d251SAlgea Cao { 235f5e7d251SAlgea Cao hdmi->write(hdmi, val, offset); 236f5e7d251SAlgea Cao } 237f5e7d251SAlgea Cao 238f5e7d251SAlgea Cao static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) 239f5e7d251SAlgea Cao { 240f5e7d251SAlgea Cao return hdmi->read(hdmi, offset); 241f5e7d251SAlgea Cao } 242f5e7d251SAlgea Cao 243f5e7d251SAlgea Cao static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) 244f5e7d251SAlgea Cao { 245f5e7d251SAlgea Cao u8 val = hdmi_readb(hdmi, reg) & ~mask; 246f5e7d251SAlgea Cao 247f5e7d251SAlgea Cao val |= data & mask; 248f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, reg); 249f5e7d251SAlgea Cao } 250f5e7d251SAlgea Cao 251f5e7d251SAlgea Cao static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, 252f5e7d251SAlgea Cao u8 shift, u8 mask) 253f5e7d251SAlgea Cao { 254f5e7d251SAlgea Cao hdmi_modb(hdmi, data << shift, mask, reg); 255f5e7d251SAlgea Cao } 256f5e7d251SAlgea Cao 257f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) 258f5e7d251SAlgea Cao { 259f5e7d251SAlgea Cao switch (bus_format) { 260f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 261f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 262f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 263f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 264f5e7d251SAlgea Cao return true; 265f5e7d251SAlgea Cao 266f5e7d251SAlgea Cao default: 267f5e7d251SAlgea Cao return false; 268f5e7d251SAlgea Cao } 269f5e7d251SAlgea Cao } 270f5e7d251SAlgea Cao 271f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) 272f5e7d251SAlgea Cao { 273f5e7d251SAlgea Cao switch (bus_format) { 274f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 275f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 276f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 277f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 278f5e7d251SAlgea Cao return true; 279f5e7d251SAlgea Cao 280f5e7d251SAlgea Cao default: 281f5e7d251SAlgea Cao return false; 282f5e7d251SAlgea Cao } 283f5e7d251SAlgea Cao } 284f5e7d251SAlgea Cao 285f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) 286f5e7d251SAlgea Cao { 287f5e7d251SAlgea Cao switch (bus_format) { 288f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 289f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 290f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 291f5e7d251SAlgea Cao return true; 292f5e7d251SAlgea Cao 293f5e7d251SAlgea Cao default: 294f5e7d251SAlgea Cao return false; 295f5e7d251SAlgea Cao } 296f5e7d251SAlgea Cao } 297f5e7d251SAlgea Cao 298f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) 299f5e7d251SAlgea Cao { 300f5e7d251SAlgea Cao switch (bus_format) { 301f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 302f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 303f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 304f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 305f5e7d251SAlgea Cao return true; 306f5e7d251SAlgea Cao 307f5e7d251SAlgea Cao default: 308f5e7d251SAlgea Cao return false; 309f5e7d251SAlgea Cao } 310f5e7d251SAlgea Cao } 311f5e7d251SAlgea Cao 312f5e7d251SAlgea Cao static int hdmi_bus_fmt_color_depth(unsigned int bus_format) 313f5e7d251SAlgea Cao { 314f5e7d251SAlgea Cao switch (bus_format) { 315f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 316f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 317f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 318f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 319f5e7d251SAlgea Cao return 8; 320f5e7d251SAlgea Cao 321f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 322f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 323f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 324f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 325f5e7d251SAlgea Cao return 10; 326f5e7d251SAlgea Cao 327f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 328f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 329f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 330f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 331f5e7d251SAlgea Cao return 12; 332f5e7d251SAlgea Cao 333f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 334f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 335f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 336f5e7d251SAlgea Cao return 16; 337f5e7d251SAlgea Cao 338f5e7d251SAlgea Cao default: 339f5e7d251SAlgea Cao return 0; 340f5e7d251SAlgea Cao } 341f5e7d251SAlgea Cao } 342f5e7d251SAlgea Cao 343f5e7d251SAlgea Cao static int is_color_space_conversion(struct dw_hdmi *hdmi) 344f5e7d251SAlgea Cao { 345b5016cf2SAlgea Cao struct drm_display_mode *mode = 346b5016cf2SAlgea Cao hdmi->edid_data.preferred_mode; 347b5016cf2SAlgea Cao bool is_cea_default; 348b5016cf2SAlgea Cao 349b5016cf2SAlgea Cao is_cea_default = (drm_match_cea_mode(mode) > 1) && 350b5016cf2SAlgea Cao (hdmi->hdmi_data.quant_range == 351b5016cf2SAlgea Cao HDMI_QUANTIZATION_RANGE_DEFAULT); 352b5016cf2SAlgea Cao 353b5016cf2SAlgea Cao /* 354b5016cf2SAlgea Cao * When output is rgb limited range or default range with 355b5016cf2SAlgea Cao * cea mode, csc should be enabled. 356b5016cf2SAlgea Cao */ 357b5016cf2SAlgea Cao if (hdmi->hdmi_data.enc_in_bus_format != 358b5016cf2SAlgea Cao hdmi->hdmi_data.enc_out_bus_format || 359b5016cf2SAlgea Cao ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED || 360b5016cf2SAlgea Cao is_cea_default) && 361b5016cf2SAlgea Cao hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format))) 362b5016cf2SAlgea Cao return 1; 363b5016cf2SAlgea Cao 364b5016cf2SAlgea Cao return 0; 365f5e7d251SAlgea Cao } 366f5e7d251SAlgea Cao 367f5e7d251SAlgea Cao static int is_color_space_decimation(struct dw_hdmi *hdmi) 368f5e7d251SAlgea Cao { 369f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 370f5e7d251SAlgea Cao return 0; 371f5e7d251SAlgea Cao 372f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) || 373f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format)) 374f5e7d251SAlgea Cao return 1; 375f5e7d251SAlgea Cao 376f5e7d251SAlgea Cao return 0; 377f5e7d251SAlgea Cao } 378f5e7d251SAlgea Cao 379f5e7d251SAlgea Cao static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, 380f5e7d251SAlgea Cao unsigned char bit) 381f5e7d251SAlgea Cao { 382f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, 383f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); 384f5e7d251SAlgea Cao } 385f5e7d251SAlgea Cao 386f5e7d251SAlgea Cao static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, 387f5e7d251SAlgea Cao unsigned char bit) 388f5e7d251SAlgea Cao { 389f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, 390f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); 391f5e7d251SAlgea Cao } 392f5e7d251SAlgea Cao 393f5e7d251SAlgea Cao static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, 394f5e7d251SAlgea Cao unsigned char bit) 395f5e7d251SAlgea Cao { 396f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, 397f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); 398f5e7d251SAlgea Cao } 399f5e7d251SAlgea Cao 400f5e7d251SAlgea Cao static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, 401f5e7d251SAlgea Cao unsigned char bit) 402f5e7d251SAlgea Cao { 403f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); 404f5e7d251SAlgea Cao } 405f5e7d251SAlgea Cao 406f5e7d251SAlgea Cao static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, 407f5e7d251SAlgea Cao unsigned char bit) 408f5e7d251SAlgea Cao { 409f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); 410f5e7d251SAlgea Cao } 411f5e7d251SAlgea Cao 4128e2bab3fSAlgea Cao static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, 4138e2bab3fSAlgea Cao unsigned char *buf, unsigned int length) 4148e2bab3fSAlgea Cao { 4158e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 4168e2bab3fSAlgea Cao int interrupt = 0, i = 20; 417*d9939fc9SAlgea Cao bool read_edid = false; 4188e2bab3fSAlgea Cao 4198e2bab3fSAlgea Cao if (!i2c->is_regaddr) { 4208e2bab3fSAlgea Cao printf("set read register address to 0\n"); 4218e2bab3fSAlgea Cao i2c->slave_reg = 0x00; 4228e2bab3fSAlgea Cao i2c->is_regaddr = true; 4238e2bab3fSAlgea Cao } 4248e2bab3fSAlgea Cao 425*d9939fc9SAlgea Cao /* edid reads are in 128 bytes. scdc reads are in 1 byte */ 426*d9939fc9SAlgea Cao if (length == HDMI_EDID_BLOCK_LEN) 427*d9939fc9SAlgea Cao read_edid = true; 428*d9939fc9SAlgea Cao 429*d9939fc9SAlgea Cao while (length > 0) { 430*d9939fc9SAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg, HDMI_I2CM_ADDRESS); 431*d9939fc9SAlgea Cao 432*d9939fc9SAlgea Cao if (read_edid) { 433*d9939fc9SAlgea Cao i2c->slave_reg += 8; 434*d9939fc9SAlgea Cao length -= 8; 435*d9939fc9SAlgea Cao } else { 436*d9939fc9SAlgea Cao i2c->slave_reg++; 437*d9939fc9SAlgea Cao length--; 438*d9939fc9SAlgea Cao } 439*d9939fc9SAlgea Cao 440*d9939fc9SAlgea Cao if (i2c->is_segment) { 441*d9939fc9SAlgea Cao if (read_edid) 442*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8_EXT, 443*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 444*d9939fc9SAlgea Cao else 4458e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, 4468e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 447*d9939fc9SAlgea Cao } else { 448*d9939fc9SAlgea Cao if (read_edid) 449*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8, 450*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 4518e2bab3fSAlgea Cao else 4528e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, 4538e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 454*d9939fc9SAlgea Cao } 4558e2bab3fSAlgea Cao 4568e2bab3fSAlgea Cao while (i--) { 4578e2bab3fSAlgea Cao udelay(1000); 4588e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0); 4598e2bab3fSAlgea Cao if (interrupt) 4608e2bab3fSAlgea Cao hdmi_writeb(hdmi, interrupt, 4618e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0); 4628e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE | 4638e2bab3fSAlgea Cao m_I2CM_ERROR)) 4648e2bab3fSAlgea Cao break; 4658e2bab3fSAlgea Cao } 4668e2bab3fSAlgea Cao 4678e2bab3fSAlgea Cao if (!interrupt) { 4688e2bab3fSAlgea Cao printf("[%s] i2c read reg[0x%02x] no interrupt\n", 4698e2bab3fSAlgea Cao __func__, i2c->slave_reg); 470*d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); 471*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, 472*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 473*d9939fc9SAlgea Cao udelay(1000); 4748e2bab3fSAlgea Cao return -EAGAIN; 4758e2bab3fSAlgea Cao } 4768e2bab3fSAlgea Cao 4778e2bab3fSAlgea Cao /* Check for error condition on the bus */ 4788e2bab3fSAlgea Cao if (interrupt & HDMI_IH_I2CM_STAT0_ERROR) { 4798e2bab3fSAlgea Cao printf("[%s] read reg[0x%02x] data error:0x%02x\n", 4808e2bab3fSAlgea Cao __func__, i2c->slave_reg, interrupt); 481*d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); 482*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, 483*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 484*d9939fc9SAlgea Cao udelay(1000); 4858e2bab3fSAlgea Cao return -EIO; 4868e2bab3fSAlgea Cao } 4878e2bab3fSAlgea Cao 4888e2bab3fSAlgea Cao i = 20; 489*d9939fc9SAlgea Cao if (read_edid) 490*d9939fc9SAlgea Cao for (i = 0; i < 8; i++) 491*d9939fc9SAlgea Cao *buf++ = hdmi_readb(hdmi, HDMI_I2CM_READ_BUFF0 + i); 492*d9939fc9SAlgea Cao else 4938e2bab3fSAlgea Cao *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); 4948e2bab3fSAlgea Cao } 4958e2bab3fSAlgea Cao i2c->is_segment = false; 4968e2bab3fSAlgea Cao 4978e2bab3fSAlgea Cao return 0; 4988e2bab3fSAlgea Cao } 4998e2bab3fSAlgea Cao 5008e2bab3fSAlgea Cao static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi, 5018e2bab3fSAlgea Cao unsigned char *buf, unsigned int length) 5028e2bab3fSAlgea Cao { 5038e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 5048e2bab3fSAlgea Cao int i = 20; 5058e2bab3fSAlgea Cao u8 interrupt = 0; 5068e2bab3fSAlgea Cao 5078e2bab3fSAlgea Cao if (!i2c->is_regaddr) { 5088e2bab3fSAlgea Cao /* Use the first write byte as register address */ 5098e2bab3fSAlgea Cao i2c->slave_reg = buf[0]; 5108e2bab3fSAlgea Cao length--; 5118e2bab3fSAlgea Cao buf++; 5128e2bab3fSAlgea Cao i2c->is_regaddr = true; 5138e2bab3fSAlgea Cao } 5148e2bab3fSAlgea Cao 5158e2bab3fSAlgea Cao while (length--) { 5168e2bab3fSAlgea Cao hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO); 5178e2bab3fSAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); 5188e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE, 5198e2bab3fSAlgea Cao HDMI_I2CM_OPERATION); 5208e2bab3fSAlgea Cao 5218e2bab3fSAlgea Cao while (i--) { 5228e2bab3fSAlgea Cao udelay(1000); 5238e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0); 5248e2bab3fSAlgea Cao if (interrupt) 5258e2bab3fSAlgea Cao hdmi_writeb(hdmi, 5268e2bab3fSAlgea Cao interrupt, HDMI_IH_I2CM_STAT0); 5278e2bab3fSAlgea Cao 5288e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ | 5298e2bab3fSAlgea Cao m_I2CM_DONE | m_I2CM_ERROR)) 5308e2bab3fSAlgea Cao break; 5318e2bab3fSAlgea Cao } 5328e2bab3fSAlgea Cao 533*d9939fc9SAlgea Cao if (!interrupt) { 534*d9939fc9SAlgea Cao printf("[%s] i2c write reg[0x%02x] no interrupt\n", 535*d9939fc9SAlgea Cao __func__, i2c->slave_reg); 536*d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); 537*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, 538*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 539*d9939fc9SAlgea Cao udelay(1000); 540*d9939fc9SAlgea Cao return -EAGAIN; 541*d9939fc9SAlgea Cao } 542*d9939fc9SAlgea Cao 5438e2bab3fSAlgea Cao if ((interrupt & m_I2CM_ERROR) || (i == -1)) { 5448e2bab3fSAlgea Cao printf("[%s] write data error\n", __func__); 545*d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); 546*d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, 547*d9939fc9SAlgea Cao HDMI_I2CM_OPERATION); 548*d9939fc9SAlgea Cao udelay(1000); 5498e2bab3fSAlgea Cao return -EIO; 5508e2bab3fSAlgea Cao } else if (interrupt & m_I2CM_DONE) { 5518e2bab3fSAlgea Cao printf("[%s] write offset %02x success\n", 5528e2bab3fSAlgea Cao __func__, i2c->slave_reg); 5538e2bab3fSAlgea Cao return -EAGAIN; 5548e2bab3fSAlgea Cao } 5558e2bab3fSAlgea Cao 5568e2bab3fSAlgea Cao i = 20; 5578e2bab3fSAlgea Cao } 5588e2bab3fSAlgea Cao 5598e2bab3fSAlgea Cao return 0; 5608e2bab3fSAlgea Cao } 5618e2bab3fSAlgea Cao 5628e2bab3fSAlgea Cao static int dw_hdmi_i2c_xfer(struct ddc_adapter *adap, 5638e2bab3fSAlgea Cao struct i2c_msg *msgs, int num) 5648e2bab3fSAlgea Cao { 5658e2bab3fSAlgea Cao struct dw_hdmi *hdmi = container_of(adap, struct dw_hdmi, adap); 5668e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c; 5678e2bab3fSAlgea Cao u8 addr = msgs[0].addr; 5688e2bab3fSAlgea Cao int i, ret = 0; 5698e2bab3fSAlgea Cao 5708e2bab3fSAlgea Cao printf("xfer: num: %d, addr: %#x\n", num, addr); 5718e2bab3fSAlgea Cao for (i = 0; i < num; i++) { 5728e2bab3fSAlgea Cao if (msgs[i].len == 0) { 5738e2bab3fSAlgea Cao printf("unsupported transfer %d/%d, no data\n", 5748e2bab3fSAlgea Cao i + 1, num); 5758e2bab3fSAlgea Cao return -EOPNOTSUPP; 5768e2bab3fSAlgea Cao } 5778e2bab3fSAlgea Cao } 5788e2bab3fSAlgea Cao 5798e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0); 5808e2bab3fSAlgea Cao 5818e2bab3fSAlgea Cao /* Set slave device address taken from the first I2C message */ 5828e2bab3fSAlgea Cao if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) 5838e2bab3fSAlgea Cao addr = DDC_ADDR; 5848e2bab3fSAlgea Cao hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE); 5858e2bab3fSAlgea Cao 5868e2bab3fSAlgea Cao /* Set slave device register address on transfer */ 5878e2bab3fSAlgea Cao i2c->is_regaddr = false; 5888e2bab3fSAlgea Cao 5898e2bab3fSAlgea Cao /* Set segment pointer for I2C extended read mode operation */ 5908e2bab3fSAlgea Cao i2c->is_segment = false; 5918e2bab3fSAlgea Cao 5928e2bab3fSAlgea Cao for (i = 0; i < num; i++) { 5938e2bab3fSAlgea Cao debug("xfer: num: %d/%d, len: %d, flags: %#x\n", 5948e2bab3fSAlgea Cao i + 1, num, msgs[i].len, msgs[i].flags); 5958e2bab3fSAlgea Cao if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { 5968e2bab3fSAlgea Cao i2c->is_segment = true; 5978e2bab3fSAlgea Cao hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR); 5988e2bab3fSAlgea Cao hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR); 5998e2bab3fSAlgea Cao } else { 6008e2bab3fSAlgea Cao if (msgs[i].flags & I2C_M_RD) 6018e2bab3fSAlgea Cao ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, 6028e2bab3fSAlgea Cao msgs[i].len); 6038e2bab3fSAlgea Cao else 6048e2bab3fSAlgea Cao ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, 6058e2bab3fSAlgea Cao msgs[i].len); 6068e2bab3fSAlgea Cao } 6078e2bab3fSAlgea Cao if (ret < 0) 6088e2bab3fSAlgea Cao break; 6098e2bab3fSAlgea Cao } 6108e2bab3fSAlgea Cao 6118e2bab3fSAlgea Cao if (!ret) 6128e2bab3fSAlgea Cao ret = num; 6138e2bab3fSAlgea Cao 6148e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */ 6158e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 6168e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0); 6178e2bab3fSAlgea Cao 6188e2bab3fSAlgea Cao return ret; 6198e2bab3fSAlgea Cao } 6208e2bab3fSAlgea Cao 621f5e7d251SAlgea Cao static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) 622f5e7d251SAlgea Cao { 623f5e7d251SAlgea Cao u32 val; 624f5e7d251SAlgea Cao 625f5e7d251SAlgea Cao while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { 626f5e7d251SAlgea Cao if (msec-- == 0) 627f5e7d251SAlgea Cao return false; 628f5e7d251SAlgea Cao udelay(1000); 629f5e7d251SAlgea Cao } 630f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0); 631f5e7d251SAlgea Cao 632f5e7d251SAlgea Cao return true; 633f5e7d251SAlgea Cao } 634f5e7d251SAlgea Cao 635f5e7d251SAlgea Cao static void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, 636f5e7d251SAlgea Cao unsigned char addr) 637f5e7d251SAlgea Cao { 638f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); 639f5e7d251SAlgea Cao hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); 640f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 8), 641f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_1_ADDR); 642f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 0), 643f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_0_ADDR); 644f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, 645f5e7d251SAlgea Cao HDMI_PHY_I2CM_OPERATION_ADDR); 646f5e7d251SAlgea Cao hdmi_phy_wait_i2c_done(hdmi, 1000); 647f5e7d251SAlgea Cao } 648f5e7d251SAlgea Cao 649f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) 650f5e7d251SAlgea Cao { 651f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, 652f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_OFFSET, 653f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_MASK); 654f5e7d251SAlgea Cao } 655f5e7d251SAlgea Cao 656f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) 657f5e7d251SAlgea Cao { 658f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 659f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_OFFSET, 660f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_MASK); 661f5e7d251SAlgea Cao } 662f5e7d251SAlgea Cao 663f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable) 664f5e7d251SAlgea Cao { 665f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 666f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_OFFSET, 667f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_MASK); 668f5e7d251SAlgea Cao } 669f5e7d251SAlgea Cao 670f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) 671f5e7d251SAlgea Cao { 672f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 673f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, 674f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_MASK); 675f5e7d251SAlgea Cao } 676f5e7d251SAlgea Cao 677f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable) 678f5e7d251SAlgea Cao { 679f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 680f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, 681f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); 682f5e7d251SAlgea Cao } 683f5e7d251SAlgea Cao 684f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable) 685f5e7d251SAlgea Cao { 686f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 687f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, 688f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_MASK); 689f5e7d251SAlgea Cao } 690f5e7d251SAlgea Cao 691f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) 692f5e7d251SAlgea Cao { 693f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, 694f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_OFFSET, 695f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_MASK); 696f5e7d251SAlgea Cao } 697f5e7d251SAlgea Cao 698f5e7d251SAlgea Cao static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) 699f5e7d251SAlgea Cao { 700f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 701f5e7d251SAlgea Cao unsigned int i; 702f5e7d251SAlgea Cao u16 val; 703f5e7d251SAlgea Cao 704f5e7d251SAlgea Cao if (phy->gen == 1) { 705f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0); 706f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, true); 707f5e7d251SAlgea Cao return; 708f5e7d251SAlgea Cao } 709f5e7d251SAlgea Cao 710f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 0); 711f5e7d251SAlgea Cao 712f5e7d251SAlgea Cao /* 713f5e7d251SAlgea Cao * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went 714f5e7d251SAlgea Cao * to low power mode. 715f5e7d251SAlgea Cao */ 716f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) { 717f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0); 718f5e7d251SAlgea Cao if (!(val & HDMI_PHY_TX_PHY_LOCK)) 719f5e7d251SAlgea Cao break; 720f5e7d251SAlgea Cao 721f5e7d251SAlgea Cao udelay(2000); 722f5e7d251SAlgea Cao } 723f5e7d251SAlgea Cao 724f5e7d251SAlgea Cao if (val & HDMI_PHY_TX_PHY_LOCK) 725f5e7d251SAlgea Cao printf("PHY failed to power down\n"); 726f5e7d251SAlgea Cao else 727f5e7d251SAlgea Cao printf("PHY powered down in %u iterations\n", i); 728f5e7d251SAlgea Cao 729f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 1); 730f5e7d251SAlgea Cao } 731f5e7d251SAlgea Cao 732f5e7d251SAlgea Cao static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) 733f5e7d251SAlgea Cao { 734f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 735f5e7d251SAlgea Cao unsigned int i; 736f5e7d251SAlgea Cao u8 val; 737f5e7d251SAlgea Cao 738f5e7d251SAlgea Cao if (phy->gen == 1) { 739f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, false); 740f5e7d251SAlgea Cao 741f5e7d251SAlgea Cao /* Toggle TMDS enable. */ 742f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0); 743f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 1); 744f5e7d251SAlgea Cao return 0; 745f5e7d251SAlgea Cao } 746f5e7d251SAlgea Cao 747f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 1); 748f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 0); 749f5e7d251SAlgea Cao 750f5e7d251SAlgea Cao /* Wait for PHY PLL lock */ 751f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) { 752f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; 753f5e7d251SAlgea Cao if (val) 754f5e7d251SAlgea Cao break; 755f5e7d251SAlgea Cao 756f5e7d251SAlgea Cao udelay(2000); 757f5e7d251SAlgea Cao } 758f5e7d251SAlgea Cao 759f5e7d251SAlgea Cao if (!val) { 760f5e7d251SAlgea Cao printf("PHY PLL failed to lock\n"); 761f5e7d251SAlgea Cao return -ETIMEDOUT; 762f5e7d251SAlgea Cao } 763f5e7d251SAlgea Cao printf("PHY PLL locked %u iterations\n", i); 7648e2bab3fSAlgea Cao 765f5e7d251SAlgea Cao return 0; 766f5e7d251SAlgea Cao } 767f5e7d251SAlgea Cao 768f5e7d251SAlgea Cao /* 769f5e7d251SAlgea Cao * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available 770f5e7d251SAlgea Cao * information the DWC MHL PHY has the same register layout and is thus also 771f5e7d251SAlgea Cao * supported by this function. 772f5e7d251SAlgea Cao */ 773f5e7d251SAlgea Cao static 774f5e7d251SAlgea Cao int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, 775f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata, 776f5e7d251SAlgea Cao unsigned long mpixelclock) 777f5e7d251SAlgea Cao { 778f5e7d251SAlgea Cao const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; 779f5e7d251SAlgea Cao const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; 780f5e7d251SAlgea Cao const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; 7818e2bab3fSAlgea Cao unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; 7828e2bab3fSAlgea Cao unsigned int depth = 7838e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 7848e2bab3fSAlgea Cao 7858e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) && 7868e2bab3fSAlgea Cao pdata->mpll_cfg_420) 7878e2bab3fSAlgea Cao mpll_config = pdata->mpll_cfg_420; 788f5e7d251SAlgea Cao 789f5e7d251SAlgea Cao /* PLL/MPLL Cfg - always match on final entry */ 790f5e7d251SAlgea Cao for (; mpll_config->mpixelclock != ~0UL; mpll_config++) 791f5e7d251SAlgea Cao if (mpixelclock <= mpll_config->mpixelclock) 792f5e7d251SAlgea Cao break; 793f5e7d251SAlgea Cao 794f5e7d251SAlgea Cao for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) 7958e2bab3fSAlgea Cao if (tmdsclock <= curr_ctrl->mpixelclock) 796f5e7d251SAlgea Cao break; 797f5e7d251SAlgea Cao 798f5e7d251SAlgea Cao for (; phy_config->mpixelclock != ~0UL; phy_config++) 7998e2bab3fSAlgea Cao if (tmdsclock <= phy_config->mpixelclock) 800f5e7d251SAlgea Cao break; 801f5e7d251SAlgea Cao 802f5e7d251SAlgea Cao if (mpll_config->mpixelclock == ~0UL || 803f5e7d251SAlgea Cao curr_ctrl->mpixelclock == ~0UL || 804f5e7d251SAlgea Cao phy_config->mpixelclock == ~0UL) 805f5e7d251SAlgea Cao return -EINVAL; 806f5e7d251SAlgea Cao 8078e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 8088e2bab3fSAlgea Cao depth = fls(depth - 8); 809f5e7d251SAlgea Cao else 8108e2bab3fSAlgea Cao depth = 0; 8118e2bab3fSAlgea Cao if (depth) 8128e2bab3fSAlgea Cao depth--; 8138e2bab3fSAlgea Cao 8148e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce, 815f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CPCE_CTRL); 8168e2bab3fSAlgea Cao 8178e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp, 818f5e7d251SAlgea Cao HDMI_3D_TX_PHY_GMPCTRL); 8198e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth], 820f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CURRCTRL); 821f5e7d251SAlgea Cao 822f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); 823f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, 824f5e7d251SAlgea Cao HDMI_3D_TX_PHY_MSM_CTRL); 825f5e7d251SAlgea Cao 8268e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); 8278e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 828f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CKSYMTXCTRL); 8298e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 830f5e7d251SAlgea Cao HDMI_3D_TX_PHY_VLEVCTRL); 831f5e7d251SAlgea Cao 832f5e7d251SAlgea Cao return 0; 833f5e7d251SAlgea Cao } 834f5e7d251SAlgea Cao 835f5e7d251SAlgea Cao static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { 836f5e7d251SAlgea Cao { 837f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, 838f5e7d251SAlgea Cao .name = "DWC HDMI TX PHY", 839f5e7d251SAlgea Cao .gen = 1, 840f5e7d251SAlgea Cao }, { 841f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC, 842f5e7d251SAlgea Cao .name = "DWC MHL PHY + HEAC PHY", 843f5e7d251SAlgea Cao .gen = 2, 844f5e7d251SAlgea Cao .has_svsret = true, 845f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 846f5e7d251SAlgea Cao }, { 847f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY, 848f5e7d251SAlgea Cao .name = "DWC MHL PHY", 849f5e7d251SAlgea Cao .gen = 2, 850f5e7d251SAlgea Cao .has_svsret = true, 851f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 852f5e7d251SAlgea Cao }, { 853f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, 854f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY + HEAC PHY", 855f5e7d251SAlgea Cao .gen = 2, 856f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 857f5e7d251SAlgea Cao }, { 858f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, 859f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY", 860f5e7d251SAlgea Cao .gen = 2, 861f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 862f5e7d251SAlgea Cao }, { 863f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, 864f5e7d251SAlgea Cao .name = "DWC HDMI 2.0 TX PHY", 865f5e7d251SAlgea Cao .gen = 2, 866f5e7d251SAlgea Cao .has_svsret = true, 867f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, 868f5e7d251SAlgea Cao }, { 869f5e7d251SAlgea Cao .type = DW_HDMI_PHY_VENDOR_PHY, 870f5e7d251SAlgea Cao .name = "Vendor PHY", 871f5e7d251SAlgea Cao } 872f5e7d251SAlgea Cao }; 873f5e7d251SAlgea Cao 874f5e7d251SAlgea Cao static int rockchip_dw_hdmi_scrambling_enable(struct dw_hdmi *hdmi, 875f5e7d251SAlgea Cao int enable) 876f5e7d251SAlgea Cao { 8778e2bab3fSAlgea Cao u8 stat; 878f5e7d251SAlgea Cao 8798e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat); 8808e2bab3fSAlgea Cao 881f5e7d251SAlgea Cao if (stat < 0) { 882f5e7d251SAlgea Cao debug("Failed to read tmds config\n"); 883f5e7d251SAlgea Cao return false; 884f5e7d251SAlgea Cao } 885f5e7d251SAlgea Cao 886f5e7d251SAlgea Cao if (enable == 1) { 887f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */ 888f5e7d251SAlgea Cao stat |= SCDC_SCRAMBLING_ENABLE; 8898e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 890f5e7d251SAlgea Cao /* TMDS software reset request */ 891f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, 892f5e7d251SAlgea Cao HDMI_MC_SWRSTZ); 893f5e7d251SAlgea Cao /* Enable/Disable Scrambling */ 894f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL); 895f5e7d251SAlgea Cao } else { 896f5e7d251SAlgea Cao /* Enable/Disable Scrambling */ 897f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL); 898f5e7d251SAlgea Cao /* TMDS software reset request */ 899f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, 900f5e7d251SAlgea Cao HDMI_MC_SWRSTZ); 901f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */ 902f5e7d251SAlgea Cao stat &= ~SCDC_SCRAMBLING_ENABLE; 9038e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 904f5e7d251SAlgea Cao } 905f5e7d251SAlgea Cao 906f5e7d251SAlgea Cao return 0; 907f5e7d251SAlgea Cao } 908f5e7d251SAlgea Cao 909f5e7d251SAlgea Cao static void rockchip_dw_hdmi_scdc_set_tmds_rate(struct dw_hdmi *hdmi) 910f5e7d251SAlgea Cao { 9118e2bab3fSAlgea Cao u8 stat; 912f5e7d251SAlgea Cao 9138e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat); 9148e2bab3fSAlgea Cao if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) 915f5e7d251SAlgea Cao stat |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 916f5e7d251SAlgea Cao else 917f5e7d251SAlgea Cao stat &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 9188e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat); 919f5e7d251SAlgea Cao } 920f5e7d251SAlgea Cao 921f5e7d251SAlgea Cao static int hdmi_phy_configure(struct dw_hdmi *hdmi) 922f5e7d251SAlgea Cao { 923f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 924f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; 925f5e7d251SAlgea Cao unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; 9268e2bab3fSAlgea Cao unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; 9278e2bab3fSAlgea Cao int ret; 928f5e7d251SAlgea Cao 929f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi); 930f5e7d251SAlgea Cao 931f5e7d251SAlgea Cao /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ 932f5e7d251SAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported) 933f5e7d251SAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi); 934f5e7d251SAlgea Cao 935f5e7d251SAlgea Cao /* Leave low power consumption mode by asserting SVSRET. */ 936f5e7d251SAlgea Cao if (phy->has_svsret) 937f5e7d251SAlgea Cao dw_hdmi_phy_enable_svsret(hdmi, 1); 938f5e7d251SAlgea Cao 939f5e7d251SAlgea Cao /* PHY reset. The reset signal is active high on Gen2 PHYs. */ 940f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); 941f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); 942f5e7d251SAlgea Cao 943f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); 944f5e7d251SAlgea Cao 945f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 1); 946f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, 947f5e7d251SAlgea Cao HDMI_PHY_I2CM_SLAVE_ADDR); 948f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 0); 949f5e7d251SAlgea Cao 950f5e7d251SAlgea Cao /* Write to the PHY as configured by the platform */ 951f5e7d251SAlgea Cao if (pdata->configure_phy) 952f5e7d251SAlgea Cao ret = pdata->configure_phy(hdmi, pdata, mpixelclock); 953f5e7d251SAlgea Cao else 954f5e7d251SAlgea Cao ret = phy->configure(hdmi, pdata, mpixelclock); 955f5e7d251SAlgea Cao if (ret) { 956f5e7d251SAlgea Cao printf("PHY configuration failed (clock %lu)\n", 957f5e7d251SAlgea Cao mpixelclock); 958f5e7d251SAlgea Cao return ret; 959f5e7d251SAlgea Cao } 960f5e7d251SAlgea Cao 961f5e7d251SAlgea Cao /* Wait for resuming transmission of TMDS clock and data */ 9628e2bab3fSAlgea Cao if (mtmdsclock > 340000000) 963f5e7d251SAlgea Cao mdelay(100); 964f5e7d251SAlgea Cao 965f5e7d251SAlgea Cao return dw_hdmi_phy_power_on(hdmi); 966f5e7d251SAlgea Cao } 967f5e7d251SAlgea Cao 9680594ce39SZhang Yubing static int dw_hdmi_phy_init(struct rockchip_connector *conn, struct dw_hdmi *hdmi, 9698e2bab3fSAlgea Cao void *data) 970f5e7d251SAlgea Cao { 971f5e7d251SAlgea Cao int i, ret; 972f5e7d251SAlgea Cao 973f5e7d251SAlgea Cao /* HDMI Phy spec says to do the phy initialization sequence twice */ 974f5e7d251SAlgea Cao for (i = 0; i < 2; i++) { 975f5e7d251SAlgea Cao dw_hdmi_phy_sel_data_en_pol(hdmi, 1); 976f5e7d251SAlgea Cao dw_hdmi_phy_sel_interface_control(hdmi, 0); 977f5e7d251SAlgea Cao ret = hdmi_phy_configure(hdmi); 978f5e7d251SAlgea Cao if (ret) 979f5e7d251SAlgea Cao return ret; 980f5e7d251SAlgea Cao } 981f5e7d251SAlgea Cao 982f5e7d251SAlgea Cao return 0; 983f5e7d251SAlgea Cao } 984f5e7d251SAlgea Cao 9850594ce39SZhang Yubing static void dw_hdmi_phy_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi, 9868e2bab3fSAlgea Cao void *data) 987f5e7d251SAlgea Cao { 988f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi); 989f5e7d251SAlgea Cao } 990f5e7d251SAlgea Cao 9918e2bab3fSAlgea Cao static enum drm_connector_status 9928e2bab3fSAlgea Cao dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data) 993f5e7d251SAlgea Cao { 994f5e7d251SAlgea Cao return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? 995f5e7d251SAlgea Cao connector_status_connected : connector_status_disconnected; 996f5e7d251SAlgea Cao } 997f5e7d251SAlgea Cao 998f5e7d251SAlgea Cao static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { 999f5e7d251SAlgea Cao .init = dw_hdmi_phy_init, 1000f5e7d251SAlgea Cao .disable = dw_hdmi_phy_disable, 1001f5e7d251SAlgea Cao .read_hpd = dw_hdmi_phy_read_hpd, 1002f5e7d251SAlgea Cao }; 1003f5e7d251SAlgea Cao 1004f5e7d251SAlgea Cao static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) 1005f5e7d251SAlgea Cao { 1006f5e7d251SAlgea Cao unsigned int i; 1007f5e7d251SAlgea Cao u8 phy_type; 1008f5e7d251SAlgea Cao 1009f5e7d251SAlgea Cao phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); 1010f5e7d251SAlgea Cao 1011f5e7d251SAlgea Cao /* 1012f5e7d251SAlgea Cao * RK3228 and RK3328 phy_type is DW_HDMI_PHY_DWC_HDMI20_TX_PHY, 1013f5e7d251SAlgea Cao * but it has a vedor phy. 1014f5e7d251SAlgea Cao */ 1015f5e7d251SAlgea Cao if (phy_type == DW_HDMI_PHY_VENDOR_PHY || 1016cb24dc0eSAlgea Cao hdmi->dev_type == RK3528_HDMI || 1017f5e7d251SAlgea Cao hdmi->dev_type == RK3328_HDMI || 1018f5e7d251SAlgea Cao hdmi->dev_type == RK3228_HDMI) { 1019f5e7d251SAlgea Cao /* Vendor PHYs require support from the glue layer. */ 1020f5e7d251SAlgea Cao if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) { 1021f5e7d251SAlgea Cao printf( 1022f5e7d251SAlgea Cao "Vendor HDMI PHY not supported by glue layer\n"); 1023f5e7d251SAlgea Cao return -ENODEV; 1024f5e7d251SAlgea Cao } 1025f5e7d251SAlgea Cao 1026f5e7d251SAlgea Cao hdmi->phy.ops = hdmi->plat_data->phy_ops; 1027f5e7d251SAlgea Cao hdmi->phy.data = hdmi->plat_data->phy_data; 1028f5e7d251SAlgea Cao hdmi->phy.name = hdmi->plat_data->phy_name; 1029f5e7d251SAlgea Cao return 0; 1030f5e7d251SAlgea Cao } 1031f5e7d251SAlgea Cao 1032f5e7d251SAlgea Cao /* Synopsys PHYs are handled internally. */ 1033f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { 1034f5e7d251SAlgea Cao if (dw_hdmi_phys[i].type == phy_type) { 1035f5e7d251SAlgea Cao hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops; 1036f5e7d251SAlgea Cao hdmi->phy.name = dw_hdmi_phys[i].name; 1037f5e7d251SAlgea Cao hdmi->phy.data = (void *)&dw_hdmi_phys[i]; 1038f5e7d251SAlgea Cao 1039f5e7d251SAlgea Cao if (!dw_hdmi_phys[i].configure && 1040f5e7d251SAlgea Cao !hdmi->plat_data->configure_phy) { 1041f5e7d251SAlgea Cao printf("%s requires platform support\n", 1042f5e7d251SAlgea Cao hdmi->phy.name); 1043f5e7d251SAlgea Cao return -ENODEV; 1044f5e7d251SAlgea Cao } 1045f5e7d251SAlgea Cao 1046f5e7d251SAlgea Cao return 0; 1047f5e7d251SAlgea Cao } 1048f5e7d251SAlgea Cao } 1049f5e7d251SAlgea Cao 1050f5e7d251SAlgea Cao printf("Unsupported HDMI PHY type (%02x)\n", phy_type); 1051f5e7d251SAlgea Cao return -ENODEV; 1052f5e7d251SAlgea Cao } 1053f5e7d251SAlgea Cao 10548e2bab3fSAlgea Cao static unsigned int 10558e2bab3fSAlgea Cao hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock) 10568e2bab3fSAlgea Cao { 10578e2bab3fSAlgea Cao unsigned int tmdsclock = mpixelclock; 10588e2bab3fSAlgea Cao unsigned int depth = 10598e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 10608e2bab3fSAlgea Cao 10618e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { 10628e2bab3fSAlgea Cao switch (depth) { 10638e2bab3fSAlgea Cao case 16: 10648e2bab3fSAlgea Cao tmdsclock = mpixelclock * 2; 10658e2bab3fSAlgea Cao break; 10668e2bab3fSAlgea Cao case 12: 10678e2bab3fSAlgea Cao tmdsclock = mpixelclock * 3 / 2; 10688e2bab3fSAlgea Cao break; 10698e2bab3fSAlgea Cao case 10: 10708e2bab3fSAlgea Cao tmdsclock = mpixelclock * 5 / 4; 10718e2bab3fSAlgea Cao break; 10728e2bab3fSAlgea Cao default: 10738e2bab3fSAlgea Cao break; 10748e2bab3fSAlgea Cao } 10758e2bab3fSAlgea Cao } 10768e2bab3fSAlgea Cao 10778e2bab3fSAlgea Cao return tmdsclock; 10788e2bab3fSAlgea Cao } 10798e2bab3fSAlgea Cao 1080f5e7d251SAlgea Cao static void hdmi_av_composer(struct dw_hdmi *hdmi, 1081f5e7d251SAlgea Cao const struct drm_display_mode *mode) 1082f5e7d251SAlgea Cao { 10838e2bab3fSAlgea Cao u8 bytes = 0, inv_val = 0; 1084f5e7d251SAlgea Cao struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; 1085f5e7d251SAlgea Cao struct drm_hdmi_info *hdmi_info = &hdmi->edid_data.display_info.hdmi; 10868e2bab3fSAlgea Cao int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; 1087f5e7d251SAlgea Cao unsigned int hdisplay, vdisplay; 1088f5e7d251SAlgea Cao 10898e2bab3fSAlgea Cao vmode->mpixelclock = mode->crtc_clock * 1000; 1090f5e7d251SAlgea Cao if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == 1091f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING) 1092f5e7d251SAlgea Cao vmode->mpixelclock *= 2; 10938e2bab3fSAlgea Cao vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); 10948e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 10958e2bab3fSAlgea Cao vmode->mtmdsclock /= 2; 10968e2bab3fSAlgea Cao printf("final pixclk = %d tmdsclk = %d\n", 10978e2bab3fSAlgea Cao vmode->mpixelclock, vmode->mtmdsclock); 1098f5e7d251SAlgea Cao 1099f5e7d251SAlgea Cao /* Set up HDMI_FC_INVIDCONF 1100f5e7d251SAlgea Cao * fc_invidconf.HDCP_keepout must be set (1'b1) 1101f5e7d251SAlgea Cao * when activate the scrambler feature. 1102f5e7d251SAlgea Cao */ 11038e2bab3fSAlgea Cao inv_val = (vmode->mtmdsclock > 340000000 || 11048e2bab3fSAlgea Cao (hdmi_info->scdc.scrambling.low_rates && 11058e2bab3fSAlgea Cao hdmi->scramble_low_rates) ? 1106f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : 1107f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); 1108f5e7d251SAlgea Cao 1109f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ? 1110f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : 1111f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW; 1112f5e7d251SAlgea Cao 1113f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ? 1114f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : 1115f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW; 1116f5e7d251SAlgea Cao 1117f5e7d251SAlgea Cao inv_val |= (vmode->mdataenablepolarity ? 1118f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : 1119f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); 1120f5e7d251SAlgea Cao 1121f5e7d251SAlgea Cao if (hdmi->vic == 39) 1122f5e7d251SAlgea Cao inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; 1123f5e7d251SAlgea Cao else 1124f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 1125f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : 1126f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW; 1127f5e7d251SAlgea Cao 1128f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 1129f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : 1130f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE; 1131f5e7d251SAlgea Cao 1132f5e7d251SAlgea Cao inv_val |= hdmi->sink_is_hdmi ? 1133f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : 1134f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE; 1135f5e7d251SAlgea Cao 1136f5e7d251SAlgea Cao hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); 1137f5e7d251SAlgea Cao 1138f5e7d251SAlgea Cao hdisplay = mode->hdisplay; 1139f5e7d251SAlgea Cao hblank = mode->htotal - mode->hdisplay; 1140f5e7d251SAlgea Cao h_de_hs = mode->hsync_start - mode->hdisplay; 1141f5e7d251SAlgea Cao hsync_len = mode->hsync_end - mode->hsync_start; 1142f5e7d251SAlgea Cao 1143f5e7d251SAlgea Cao /* 1144f5e7d251SAlgea Cao * When we're setting a YCbCr420 mode, we need 1145f5e7d251SAlgea Cao * to adjust the horizontal timing to suit. 1146f5e7d251SAlgea Cao */ 1147f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { 1148f5e7d251SAlgea Cao hdisplay /= 2; 1149f5e7d251SAlgea Cao hblank /= 2; 1150f5e7d251SAlgea Cao h_de_hs /= 2; 1151f5e7d251SAlgea Cao hsync_len /= 2; 1152f5e7d251SAlgea Cao } 1153f5e7d251SAlgea Cao 1154f5e7d251SAlgea Cao vdisplay = mode->vdisplay; 1155f5e7d251SAlgea Cao vblank = mode->vtotal - mode->vdisplay; 1156f5e7d251SAlgea Cao v_de_vs = mode->vsync_start - mode->vdisplay; 1157f5e7d251SAlgea Cao vsync_len = mode->vsync_end - mode->vsync_start; 1158f5e7d251SAlgea Cao 1159f5e7d251SAlgea Cao /* 1160f5e7d251SAlgea Cao * When we're setting an interlaced mode, we need 1161f5e7d251SAlgea Cao * to adjust the vertical timing to suit. 1162f5e7d251SAlgea Cao */ 1163f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 1164f5e7d251SAlgea Cao vdisplay /= 2; 1165f5e7d251SAlgea Cao vblank /= 2; 1166f5e7d251SAlgea Cao v_de_vs /= 2; 1167f5e7d251SAlgea Cao vsync_len /= 2; 1168f5e7d251SAlgea Cao } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == 1169f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING) { 1170f5e7d251SAlgea Cao vdisplay += mode->vtotal; 1171f5e7d251SAlgea Cao } 1172f5e7d251SAlgea Cao 1173f5e7d251SAlgea Cao /* Scrambling Control */ 1174f5e7d251SAlgea Cao if (hdmi_info->scdc.supported) { 11758e2bab3fSAlgea Cao if (vmode->mtmdsclock > 340000000 || 11768e2bab3fSAlgea Cao (hdmi_info->scdc.scrambling.low_rates && 11778e2bab3fSAlgea Cao hdmi->scramble_low_rates)) { 11788e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_SINK_VERSION, &bytes); 11798e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_SOURCE_VERSION, 11808e2bab3fSAlgea Cao bytes); 1181f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 1); 1182f5e7d251SAlgea Cao } else { 1183f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 0); 1184f5e7d251SAlgea Cao } 1185f5e7d251SAlgea Cao } 1186f5e7d251SAlgea Cao 1187f5e7d251SAlgea Cao /* Set up horizontal active pixel width */ 1188f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1); 1189f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0); 1190f5e7d251SAlgea Cao 1191f5e7d251SAlgea Cao /* Set up vertical active lines */ 1192f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1); 1193f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0); 1194f5e7d251SAlgea Cao 1195f5e7d251SAlgea Cao /* Set up horizontal blanking pixel region width */ 1196f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); 1197f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); 1198f5e7d251SAlgea Cao 1199f5e7d251SAlgea Cao /* Set up vertical blanking pixel region width */ 1200f5e7d251SAlgea Cao hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); 1201f5e7d251SAlgea Cao 1202f5e7d251SAlgea Cao /* Set up HSYNC active edge delay width (in pixel clks) */ 1203f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); 1204f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); 1205f5e7d251SAlgea Cao 1206f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */ 1207f5e7d251SAlgea Cao hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); 1208f5e7d251SAlgea Cao 1209f5e7d251SAlgea Cao /* Set up HSYNC active pulse width (in pixel clks) */ 1210f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); 1211f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); 1212f5e7d251SAlgea Cao 1213f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */ 1214f5e7d251SAlgea Cao hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); 1215f5e7d251SAlgea Cao } 1216f5e7d251SAlgea Cao 1217f5e7d251SAlgea Cao static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) 1218f5e7d251SAlgea Cao { 1219f5e7d251SAlgea Cao const u16 (*csc_coeff)[3][4] = &csc_coeff_default; 1220f5e7d251SAlgea Cao unsigned i; 1221f5e7d251SAlgea Cao u32 csc_scale = 1; 1222b5016cf2SAlgea Cao int enc_out_rgb, enc_in_rgb; 1223b5016cf2SAlgea Cao 1224b5016cf2SAlgea Cao enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); 1225b5016cf2SAlgea Cao enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); 1226f5e7d251SAlgea Cao 1227f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) { 12283f6d16abSAlgea Cao if (enc_out_rgb && enc_in_rgb) { 1229b5016cf2SAlgea Cao csc_coeff = &csc_coeff_full_to_limited; 1230b5016cf2SAlgea Cao csc_scale = 0; 1231b5016cf2SAlgea Cao } else if (enc_out_rgb) { 1232f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding == 1233f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601) 1234f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu601; 1235f5e7d251SAlgea Cao else 1236f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu709; 1237b5016cf2SAlgea Cao } else if (enc_in_rgb) { 1238f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding == 1239f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601) 1240f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu601; 1241f5e7d251SAlgea Cao else 1242f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu709; 1243f5e7d251SAlgea Cao csc_scale = 0; 1244f5e7d251SAlgea Cao } 1245f5e7d251SAlgea Cao } 1246f5e7d251SAlgea Cao 1247f5e7d251SAlgea Cao /* The CSC registers are sequential, alternating MSB then LSB */ 1248f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { 1249f5e7d251SAlgea Cao u16 coeff_a = (*csc_coeff)[0][i]; 1250f5e7d251SAlgea Cao u16 coeff_b = (*csc_coeff)[1][i]; 1251f5e7d251SAlgea Cao u16 coeff_c = (*csc_coeff)[2][i]; 1252f5e7d251SAlgea Cao 1253f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); 1254f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); 1255f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); 1256f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); 1257f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); 1258f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); 1259f5e7d251SAlgea Cao } 1260f5e7d251SAlgea Cao 1261f5e7d251SAlgea Cao hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, 1262f5e7d251SAlgea Cao HDMI_CSC_SCALE); 1263f5e7d251SAlgea Cao } 1264f5e7d251SAlgea Cao 1265f5e7d251SAlgea Cao static int is_color_space_interpolation(struct dw_hdmi *hdmi) 1266f5e7d251SAlgea Cao { 1267f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format)) 1268f5e7d251SAlgea Cao return 0; 1269f5e7d251SAlgea Cao 1270f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || 1271f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) 1272f5e7d251SAlgea Cao return 1; 1273f5e7d251SAlgea Cao 1274f5e7d251SAlgea Cao return 0; 1275f5e7d251SAlgea Cao } 1276f5e7d251SAlgea Cao 1277f5e7d251SAlgea Cao static void hdmi_video_csc(struct dw_hdmi *hdmi) 1278f5e7d251SAlgea Cao { 1279f5e7d251SAlgea Cao int color_depth = 0; 1280f5e7d251SAlgea Cao int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; 1281f5e7d251SAlgea Cao int decimation = 0; 1282f5e7d251SAlgea Cao 1283f5e7d251SAlgea Cao /* YCC422 interpolation to 444 mode */ 1284f5e7d251SAlgea Cao if (is_color_space_interpolation(hdmi)) 1285f5e7d251SAlgea Cao interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; 1286f5e7d251SAlgea Cao else if (is_color_space_decimation(hdmi)) 1287f5e7d251SAlgea Cao decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; 1288f5e7d251SAlgea Cao 1289f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { 1290f5e7d251SAlgea Cao case 8: 1291f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; 1292f5e7d251SAlgea Cao break; 1293f5e7d251SAlgea Cao case 10: 1294f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; 1295f5e7d251SAlgea Cao break; 1296f5e7d251SAlgea Cao case 12: 1297f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; 1298f5e7d251SAlgea Cao break; 1299f5e7d251SAlgea Cao case 16: 1300f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; 1301f5e7d251SAlgea Cao break; 1302f5e7d251SAlgea Cao 1303f5e7d251SAlgea Cao default: 1304f5e7d251SAlgea Cao return; 1305f5e7d251SAlgea Cao } 1306f5e7d251SAlgea Cao 1307f5e7d251SAlgea Cao /* Configure the CSC registers */ 1308f5e7d251SAlgea Cao hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); 1309f5e7d251SAlgea Cao hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, 1310f5e7d251SAlgea Cao HDMI_CSC_SCALE); 1311f5e7d251SAlgea Cao 1312f5e7d251SAlgea Cao dw_hdmi_update_csc_coeffs(hdmi); 1313f5e7d251SAlgea Cao } 1314f5e7d251SAlgea Cao 1315f5e7d251SAlgea Cao static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) 1316f5e7d251SAlgea Cao { 1317f5e7d251SAlgea Cao u8 clkdis; 1318f5e7d251SAlgea Cao 1319f5e7d251SAlgea Cao /* control period minimum duration */ 1320f5e7d251SAlgea Cao hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); 1321f5e7d251SAlgea Cao hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); 1322f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC); 1323f5e7d251SAlgea Cao 1324f5e7d251SAlgea Cao /* Set to fill TMDS data channels */ 1325f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM); 1326f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM); 1327f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); 1328f5e7d251SAlgea Cao 1329f5e7d251SAlgea Cao /* Enable pixel clock and tmds data path */ 1330f5e7d251SAlgea Cao clkdis = 0x7F; 1331f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; 1332f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1333f5e7d251SAlgea Cao 1334f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; 1335f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1336f5e7d251SAlgea Cao 1337f5e7d251SAlgea Cao /* Enable csc path */ 1338f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) { 1339f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; 1340f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1341f5e7d251SAlgea Cao } 1342f5e7d251SAlgea Cao 1343f5e7d251SAlgea Cao /* Enable pixel repetition path */ 1344f5e7d251SAlgea Cao if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) { 1345f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; 1346f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); 1347f5e7d251SAlgea Cao } 1348f5e7d251SAlgea Cao 1349f5e7d251SAlgea Cao /* Enable color space conversion if needed */ 1350f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) 1351f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH, 1352f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL); 1353f5e7d251SAlgea Cao else 1354f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS, 1355f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL); 1356f5e7d251SAlgea Cao } 1357f5e7d251SAlgea Cao 1358f5e7d251SAlgea Cao static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) 1359f5e7d251SAlgea Cao { 1360f5e7d251SAlgea Cao unsigned int count; 1361f5e7d251SAlgea Cao unsigned int i; 1362f5e7d251SAlgea Cao u8 val; 1363f5e7d251SAlgea Cao 1364f5e7d251SAlgea Cao /* 1365f5e7d251SAlgea Cao * Under some circumstances the Frame Composer arithmetic unit can miss 1366f5e7d251SAlgea Cao * an FC register write due to being busy processing the previous one. 1367f5e7d251SAlgea Cao * The issue can be worked around by issuing a TMDS software reset and 1368f5e7d251SAlgea Cao * then write one of the FC registers several times. 1369f5e7d251SAlgea Cao * 1370f5e7d251SAlgea Cao * The number of iterations matters and depends on the HDMI TX revision 1371f5e7d251SAlgea Cao * (and possibly on the platform). So far only i.MX6Q (v1.30a) and 1372f5e7d251SAlgea Cao * i.MX6DL (v1.31a) have been identified as needing the workaround, with 1373f5e7d251SAlgea Cao * 4 and 1 iterations respectively. 1374f5e7d251SAlgea Cao */ 1375f5e7d251SAlgea Cao 1376f5e7d251SAlgea Cao switch (hdmi->version) { 1377f5e7d251SAlgea Cao case 0x130a: 1378f5e7d251SAlgea Cao count = 4; 1379f5e7d251SAlgea Cao break; 1380f5e7d251SAlgea Cao case 0x131a: 13818e2bab3fSAlgea Cao case 0x200a: 13828e2bab3fSAlgea Cao case 0x201a: 13838e2bab3fSAlgea Cao case 0x211a: 1384f5e7d251SAlgea Cao count = 1; 1385f5e7d251SAlgea Cao break; 1386f5e7d251SAlgea Cao default: 1387f5e7d251SAlgea Cao return; 1388f5e7d251SAlgea Cao } 1389f5e7d251SAlgea Cao 1390f5e7d251SAlgea Cao /* TMDS software reset */ 1391f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); 1392f5e7d251SAlgea Cao 1393f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); 1394f5e7d251SAlgea Cao for (i = 0; i < count; i++) 1395f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); 1396f5e7d251SAlgea Cao } 1397f5e7d251SAlgea Cao 1398f5e7d251SAlgea Cao static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) 1399f5e7d251SAlgea Cao { 1400f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, 1401f5e7d251SAlgea Cao HDMI_IH_MUTE_FC_STAT2); 1402f5e7d251SAlgea Cao } 1403f5e7d251SAlgea Cao 1404f5e7d251SAlgea Cao static void hdmi_video_packetize(struct dw_hdmi *hdmi) 1405f5e7d251SAlgea Cao { 1406f5e7d251SAlgea Cao unsigned int color_depth = 0; 1407f5e7d251SAlgea Cao unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; 1408f5e7d251SAlgea Cao unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; 1409f5e7d251SAlgea Cao struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; 1410f5e7d251SAlgea Cao u8 val, vp_conf; 1411f5e7d251SAlgea Cao 1412f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || 1413f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || 1414f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { 1415f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth( 1416f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) { 1417f5e7d251SAlgea Cao case 8: 1418f5e7d251SAlgea Cao color_depth = 0; 1419f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; 1420f5e7d251SAlgea Cao break; 1421f5e7d251SAlgea Cao case 10: 1422f5e7d251SAlgea Cao color_depth = 5; 1423f5e7d251SAlgea Cao break; 1424f5e7d251SAlgea Cao case 12: 1425f5e7d251SAlgea Cao color_depth = 6; 1426f5e7d251SAlgea Cao break; 1427f5e7d251SAlgea Cao case 16: 1428f5e7d251SAlgea Cao color_depth = 7; 1429f5e7d251SAlgea Cao break; 1430f5e7d251SAlgea Cao default: 1431f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; 1432f5e7d251SAlgea Cao } 1433f5e7d251SAlgea Cao } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { 1434f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth( 1435f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) { 1436f5e7d251SAlgea Cao case 0: 1437f5e7d251SAlgea Cao case 8: 1438f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_16bit; 1439f5e7d251SAlgea Cao break; 1440f5e7d251SAlgea Cao case 10: 1441f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_20bit; 1442f5e7d251SAlgea Cao break; 1443f5e7d251SAlgea Cao case 12: 1444f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_24bit; 1445f5e7d251SAlgea Cao break; 1446f5e7d251SAlgea Cao 1447f5e7d251SAlgea Cao default: 1448f5e7d251SAlgea Cao return; 1449f5e7d251SAlgea Cao } 1450f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; 1451f5e7d251SAlgea Cao } else { 1452f5e7d251SAlgea Cao return; 1453f5e7d251SAlgea Cao } 1454f5e7d251SAlgea Cao 1455f5e7d251SAlgea Cao /* set the packetizer registers */ 14568e2bab3fSAlgea Cao val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & 14578e2bab3fSAlgea Cao HDMI_VP_PR_CD_COLOR_DEPTH_MASK; 1458f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); 1459f5e7d251SAlgea Cao 1460f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, 1461f5e7d251SAlgea Cao HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); 1462f5e7d251SAlgea Cao 1463f5e7d251SAlgea Cao /* Data from pixel repeater block */ 1464f5e7d251SAlgea Cao if (hdmi_data->pix_repet_factor > 0) { 1465f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | 1466f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; 1467f5e7d251SAlgea Cao } else { /* data from packetizer block */ 1468f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | 1469f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; 1470f5e7d251SAlgea Cao } 1471f5e7d251SAlgea Cao 1472f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf, 1473f5e7d251SAlgea Cao HDMI_VP_CONF_PR_EN_MASK | 1474f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); 1475f5e7d251SAlgea Cao 14768e2bab3fSAlgea Cao if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) || 14778e2bab3fSAlgea Cao (color_depth == 6 && hdmi->previous_mode.htotal % 2)) 14788e2bab3fSAlgea Cao hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, 14798e2bab3fSAlgea Cao HDMI_VP_STUFF); 14808e2bab3fSAlgea Cao else 1481f5e7d251SAlgea Cao hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, 1482f5e7d251SAlgea Cao HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); 1483f5e7d251SAlgea Cao 1484f5e7d251SAlgea Cao hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); 1485f5e7d251SAlgea Cao 1486f5e7d251SAlgea Cao if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { 1487f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | 1488f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_ENABLE | 1489f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE; 1490f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { 1491f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | 1492f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE | 1493f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_ENABLE; 1494f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { 1495f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | 1496f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE | 1497f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE; 1498f5e7d251SAlgea Cao } else { 1499f5e7d251SAlgea Cao return; 1500f5e7d251SAlgea Cao } 1501f5e7d251SAlgea Cao 1502f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf, 1503f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | 1504f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); 1505f5e7d251SAlgea Cao 1506f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | 1507f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, 1508f5e7d251SAlgea Cao HDMI_VP_STUFF_PP_STUFFING_MASK | 1509f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); 1510f5e7d251SAlgea Cao 1511f5e7d251SAlgea Cao hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, 1512f5e7d251SAlgea Cao HDMI_VP_CONF); 1513f5e7d251SAlgea Cao } 1514f5e7d251SAlgea Cao 1515f5e7d251SAlgea Cao static void hdmi_video_sample(struct dw_hdmi *hdmi) 1516f5e7d251SAlgea Cao { 1517f5e7d251SAlgea Cao int color_format = 0; 1518f5e7d251SAlgea Cao u8 val; 1519f5e7d251SAlgea Cao 1520f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_in_bus_format) { 1521f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24: 1522f5e7d251SAlgea Cao color_format = 0x01; 1523f5e7d251SAlgea Cao break; 1524f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30: 1525f5e7d251SAlgea Cao color_format = 0x03; 1526f5e7d251SAlgea Cao break; 1527f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36: 1528f5e7d251SAlgea Cao color_format = 0x05; 1529f5e7d251SAlgea Cao break; 1530f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48: 1531f5e7d251SAlgea Cao color_format = 0x07; 1532f5e7d251SAlgea Cao break; 1533f5e7d251SAlgea Cao 1534f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24: 1535f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 1536f5e7d251SAlgea Cao color_format = 0x09; 1537f5e7d251SAlgea Cao break; 1538f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30: 1539f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 1540f5e7d251SAlgea Cao color_format = 0x0B; 1541f5e7d251SAlgea Cao break; 1542f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36: 1543f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 1544f5e7d251SAlgea Cao color_format = 0x0D; 1545f5e7d251SAlgea Cao break; 1546f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48: 1547f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 1548f5e7d251SAlgea Cao color_format = 0x0F; 1549f5e7d251SAlgea Cao break; 1550f5e7d251SAlgea Cao 1551f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 1552f5e7d251SAlgea Cao color_format = 0x16; 1553f5e7d251SAlgea Cao break; 1554f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 1555f5e7d251SAlgea Cao color_format = 0x14; 1556f5e7d251SAlgea Cao break; 1557f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24: 1558f5e7d251SAlgea Cao color_format = 0x12; 1559f5e7d251SAlgea Cao break; 1560f5e7d251SAlgea Cao 1561f5e7d251SAlgea Cao default: 1562f5e7d251SAlgea Cao return; 1563f5e7d251SAlgea Cao } 1564f5e7d251SAlgea Cao 1565f5e7d251SAlgea Cao val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | 1566f5e7d251SAlgea Cao ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & 1567f5e7d251SAlgea Cao HDMI_TX_INVID0_VIDEO_MAPPING_MASK); 1568f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INVID0); 1569f5e7d251SAlgea Cao 1570f5e7d251SAlgea Cao /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */ 1571f5e7d251SAlgea Cao val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | 1572f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | 1573f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; 1574f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING); 1575f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0); 1576f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1); 1577f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0); 1578f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1); 1579f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0); 1580f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1); 1581f5e7d251SAlgea Cao } 1582f5e7d251SAlgea Cao 15830594ce39SZhang Yubing static void dw_hdmi_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi, 15840594ce39SZhang Yubing struct display_state *state) 1585f5e7d251SAlgea Cao { 1586f5e7d251SAlgea Cao if (hdmi->phy.enabled) { 15870594ce39SZhang Yubing hdmi->phy.ops->disable(conn, hdmi, state); 1588f5e7d251SAlgea Cao hdmi->phy.enabled = false; 1589f5e7d251SAlgea Cao } 1590f5e7d251SAlgea Cao } 1591f5e7d251SAlgea Cao 1592f5e7d251SAlgea Cao static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) 1593f5e7d251SAlgea Cao { 1594f5e7d251SAlgea Cao struct hdmi_avi_infoframe frame; 1595f5e7d251SAlgea Cao u8 val; 1596f5e7d251SAlgea Cao bool is_hdmi2 = false; 1597b5016cf2SAlgea Cao enum hdmi_quantization_range rgb_quant_range = 1598b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range; 1599f5e7d251SAlgea Cao 1600f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || 1601f5e7d251SAlgea Cao hdmi->edid_data.display_info.hdmi.scdc.supported) 1602f5e7d251SAlgea Cao is_hdmi2 = true; 1603f5e7d251SAlgea Cao /* Initialise info frame from DRM mode */ 1604f5e7d251SAlgea Cao drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2); 1605f5e7d251SAlgea Cao 1606b5016cf2SAlgea Cao /* 1607b5016cf2SAlgea Cao * Ignore monitor selectable quantization, use quantization set 1608b5016cf2SAlgea Cao * by the user 1609b5016cf2SAlgea Cao */ 1610b5016cf2SAlgea Cao drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range, 1611b5016cf2SAlgea Cao true); 1612f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) 1613f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV444; 1614f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 1615f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV422; 1616f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 1617f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV420; 1618f5e7d251SAlgea Cao else 1619f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_RGB; 1620f5e7d251SAlgea Cao 1621f5e7d251SAlgea Cao /* Set up colorimetry */ 1622f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_out_encoding) { 1623f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_601: 1624f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) 1625f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; 1626f5e7d251SAlgea Cao else 1627f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601; 1628f5e7d251SAlgea Cao frame.extended_colorimetry = 1629f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 1630f5e7d251SAlgea Cao break; 1631f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_709: 1632f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) 1633f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; 1634f5e7d251SAlgea Cao else 1635f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_709; 1636f5e7d251SAlgea Cao frame.extended_colorimetry = 1637f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; 1638f5e7d251SAlgea Cao break; 1639f5e7d251SAlgea Cao default: /* Carries no data */ 1640f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601; 1641f5e7d251SAlgea Cao frame.extended_colorimetry = 1642f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 1643f5e7d251SAlgea Cao break; 1644f5e7d251SAlgea Cao } 1645f5e7d251SAlgea Cao 1646f5e7d251SAlgea Cao frame.scan_mode = HDMI_SCAN_MODE_NONE; 1647f5e7d251SAlgea Cao 1648f5e7d251SAlgea Cao /* 1649f5e7d251SAlgea Cao * The Designware IP uses a different byte format from standard 1650f5e7d251SAlgea Cao * AVI info frames, though generally the bits are in the correct 1651f5e7d251SAlgea Cao * bytes. 1652f5e7d251SAlgea Cao */ 1653f5e7d251SAlgea Cao 1654f5e7d251SAlgea Cao /* 1655f5e7d251SAlgea Cao * AVI data byte 1 differences: Colorspace in bits 0,1,7 rather than 1656f5e7d251SAlgea Cao * 5,6,7, active aspect present in bit 6 rather than 4. 1657f5e7d251SAlgea Cao */ 1658f5e7d251SAlgea Cao val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 0x3); 1659f5e7d251SAlgea Cao if (frame.active_aspect & 15) 1660f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT; 1661f5e7d251SAlgea Cao if (frame.top_bar || frame.bottom_bar) 1662f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR; 1663f5e7d251SAlgea Cao if (frame.left_bar || frame.right_bar) 1664f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR; 1665f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0); 1666f5e7d251SAlgea Cao 1667f5e7d251SAlgea Cao /* AVI data byte 2 differences: none */ 1668f5e7d251SAlgea Cao val = ((frame.colorimetry & 0x3) << 6) | 1669f5e7d251SAlgea Cao ((frame.picture_aspect & 0x3) << 4) | 1670f5e7d251SAlgea Cao (frame.active_aspect & 0xf); 1671f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1); 1672f5e7d251SAlgea Cao 1673f5e7d251SAlgea Cao /* AVI data byte 3 differences: none */ 1674f5e7d251SAlgea Cao val = ((frame.extended_colorimetry & 0x7) << 4) | 1675f5e7d251SAlgea Cao ((frame.quantization_range & 0x3) << 2) | 1676f5e7d251SAlgea Cao (frame.nups & 0x3); 1677f5e7d251SAlgea Cao if (frame.itc) 1678f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID; 1679f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); 1680f5e7d251SAlgea Cao 1681f5e7d251SAlgea Cao /* AVI data byte 4 differences: none */ 1682f5e7d251SAlgea Cao val = frame.video_code & 0x7f; 1683f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVIVID); 1684f5e7d251SAlgea Cao 1685f5e7d251SAlgea Cao /* AVI Data Byte 5- set up input and output pixel repetition */ 1686f5e7d251SAlgea Cao val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) << 1687f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) & 1688f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) | 1689f5e7d251SAlgea Cao ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput << 1690f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) & 1691f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK); 1692f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_PRCONF); 1693f5e7d251SAlgea Cao 1694f5e7d251SAlgea Cao /* 1695f5e7d251SAlgea Cao * AVI data byte 5 differences: content type in 0,1 rather than 4,5, 1696f5e7d251SAlgea Cao * ycc range in bits 2,3 rather than 6,7 1697f5e7d251SAlgea Cao */ 1698f5e7d251SAlgea Cao val = ((frame.ycc_quantization_range & 0x3) << 2) | 1699f5e7d251SAlgea Cao (frame.content_type & 0x3); 1700f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3); 1701f5e7d251SAlgea Cao 1702f5e7d251SAlgea Cao /* AVI Data Bytes 6-13 */ 1703f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0); 1704f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1); 1705f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0); 1706f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1); 1707f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0); 1708f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1); 1709f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0); 1710f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1); 1711f5e7d251SAlgea Cao } 1712f5e7d251SAlgea Cao 1713f5e7d251SAlgea Cao static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, 1714f5e7d251SAlgea Cao struct drm_display_mode *mode) 1715f5e7d251SAlgea Cao { 1716f5e7d251SAlgea Cao struct hdmi_vendor_infoframe frame; 1717f5e7d251SAlgea Cao u8 buffer[10]; 1718f5e7d251SAlgea Cao ssize_t err; 1719f5e7d251SAlgea Cao 1720f5e7d251SAlgea Cao /* Disable HDMI vendor specific infoframe send */ 1721f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, 1722f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK); 1723f5e7d251SAlgea Cao 1724f5e7d251SAlgea Cao err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode); 1725f5e7d251SAlgea Cao if (err < 0) 1726f5e7d251SAlgea Cao /* 1727f5e7d251SAlgea Cao * Going into that statement does not means vendor infoframe 1728f5e7d251SAlgea Cao * fails. It just informed us that vendor infoframe is not 1729f5e7d251SAlgea Cao * needed for the selected mode. Only 4k or stereoscopic 3D 1730f5e7d251SAlgea Cao * mode requires vendor infoframe. So just simply return. 1731f5e7d251SAlgea Cao */ 1732f5e7d251SAlgea Cao return; 1733f5e7d251SAlgea Cao 1734f5e7d251SAlgea Cao err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 1735f5e7d251SAlgea Cao if (err < 0) { 1736f5e7d251SAlgea Cao printf("Failed to pack vendor infoframe: %zd\n", err); 1737f5e7d251SAlgea Cao return; 1738f5e7d251SAlgea Cao } 1739f5e7d251SAlgea Cao 1740f5e7d251SAlgea Cao /* Set the length of HDMI vendor specific InfoFrame payload */ 1741f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE); 1742f5e7d251SAlgea Cao 1743f5e7d251SAlgea Cao /* Set 24bit IEEE Registration Identifier */ 1744f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0); 1745f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1); 1746f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2); 1747f5e7d251SAlgea Cao 1748f5e7d251SAlgea Cao /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */ 1749f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0); 1750f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1); 1751f5e7d251SAlgea Cao 1752f5e7d251SAlgea Cao if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) 1753f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2); 1754f5e7d251SAlgea Cao 1755f5e7d251SAlgea Cao /* Packet frame interpolation */ 1756f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1); 1757f5e7d251SAlgea Cao 1758f5e7d251SAlgea Cao /* Auto packets per frame and line spacing */ 1759f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2); 1760f5e7d251SAlgea Cao 1761f5e7d251SAlgea Cao /* Configures the Frame Composer On RDRB mode */ 1762f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, 1763f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK); 1764f5e7d251SAlgea Cao } 1765f5e7d251SAlgea Cao 1766f5e7d251SAlgea Cao static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, 1767f5e7d251SAlgea Cao unsigned int n) 1768f5e7d251SAlgea Cao { 1769f5e7d251SAlgea Cao /* Must be set/cleared first */ 1770f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); 1771f5e7d251SAlgea Cao 1772f5e7d251SAlgea Cao /* nshift factor = 0 */ 1773f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); 1774f5e7d251SAlgea Cao 1775f5e7d251SAlgea Cao hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | 1776f5e7d251SAlgea Cao HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); 1777f5e7d251SAlgea Cao hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); 1778f5e7d251SAlgea Cao hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); 1779f5e7d251SAlgea Cao 1780f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3); 1781f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); 1782f5e7d251SAlgea Cao hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); 1783f5e7d251SAlgea Cao } 1784f5e7d251SAlgea Cao 1785f5e7d251SAlgea Cao static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi, 1786f5e7d251SAlgea Cao unsigned long pixel_clk, 1787f5e7d251SAlgea Cao unsigned long freq) 1788f5e7d251SAlgea Cao { 1789f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; 1790f5e7d251SAlgea Cao const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; 1791f5e7d251SAlgea Cao int i; 1792f5e7d251SAlgea Cao 1793f5e7d251SAlgea Cao if (plat_data->tmds_n_table) { 1794f5e7d251SAlgea Cao for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { 1795f5e7d251SAlgea Cao if (pixel_clk == plat_data->tmds_n_table[i].tmds) { 1796f5e7d251SAlgea Cao tmds_n = &plat_data->tmds_n_table[i]; 1797f5e7d251SAlgea Cao break; 1798f5e7d251SAlgea Cao } 1799f5e7d251SAlgea Cao } 1800f5e7d251SAlgea Cao } 1801f5e7d251SAlgea Cao 1802f5e7d251SAlgea Cao if (!tmds_n) { 1803f5e7d251SAlgea Cao for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { 1804f5e7d251SAlgea Cao if (pixel_clk == common_tmds_n_table[i].tmds) { 1805f5e7d251SAlgea Cao tmds_n = &common_tmds_n_table[i]; 1806f5e7d251SAlgea Cao break; 1807f5e7d251SAlgea Cao } 1808f5e7d251SAlgea Cao } 1809f5e7d251SAlgea Cao } 1810f5e7d251SAlgea Cao 1811f5e7d251SAlgea Cao if (!tmds_n) 1812f5e7d251SAlgea Cao return -ENOENT; 1813f5e7d251SAlgea Cao 1814f5e7d251SAlgea Cao switch (freq) { 1815f5e7d251SAlgea Cao case 32000: 1816f5e7d251SAlgea Cao return tmds_n->n_32k; 1817f5e7d251SAlgea Cao case 44100: 1818f5e7d251SAlgea Cao case 88200: 1819f5e7d251SAlgea Cao case 176400: 1820f5e7d251SAlgea Cao return (freq / 44100) * tmds_n->n_44k1; 1821f5e7d251SAlgea Cao case 48000: 1822f5e7d251SAlgea Cao case 96000: 1823f5e7d251SAlgea Cao case 192000: 1824f5e7d251SAlgea Cao return (freq / 48000) * tmds_n->n_48k; 1825f5e7d251SAlgea Cao default: 1826f5e7d251SAlgea Cao return -ENOENT; 1827f5e7d251SAlgea Cao } 1828f5e7d251SAlgea Cao } 1829f5e7d251SAlgea Cao 1830f5e7d251SAlgea Cao static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, 1831f5e7d251SAlgea Cao unsigned int pixel_clk) 1832f5e7d251SAlgea Cao { 1833f5e7d251SAlgea Cao u64 final, diff; 1834f5e7d251SAlgea Cao u64 cts; 1835f5e7d251SAlgea Cao 1836f5e7d251SAlgea Cao final = (u64)pixel_clk * n; 1837f5e7d251SAlgea Cao 1838f5e7d251SAlgea Cao cts = final; 1839f5e7d251SAlgea Cao do_div(cts, 128 * freq); 1840f5e7d251SAlgea Cao 1841f5e7d251SAlgea Cao diff = final - (u64)cts * (128 * freq); 1842f5e7d251SAlgea Cao 1843f5e7d251SAlgea Cao return diff; 1844f5e7d251SAlgea Cao } 1845f5e7d251SAlgea Cao 1846f5e7d251SAlgea Cao static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi, 1847f5e7d251SAlgea Cao unsigned long pixel_clk, 1848f5e7d251SAlgea Cao unsigned long freq) 1849f5e7d251SAlgea Cao { 1850f5e7d251SAlgea Cao unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); 1851f5e7d251SAlgea Cao unsigned int max_n = (128 * freq) / 300; 1852f5e7d251SAlgea Cao unsigned int ideal_n = (128 * freq) / 1000; 1853f5e7d251SAlgea Cao unsigned int best_n_distance = ideal_n; 1854f5e7d251SAlgea Cao unsigned int best_n = 0; 1855f5e7d251SAlgea Cao u64 best_diff = U64_MAX; 1856f5e7d251SAlgea Cao int n; 1857f5e7d251SAlgea Cao 1858f5e7d251SAlgea Cao /* If the ideal N could satisfy the audio math, then just take it */ 1859f5e7d251SAlgea Cao if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) 1860f5e7d251SAlgea Cao return ideal_n; 1861f5e7d251SAlgea Cao 1862f5e7d251SAlgea Cao for (n = min_n; n <= max_n; n++) { 1863f5e7d251SAlgea Cao u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); 1864f5e7d251SAlgea Cao 1865f5e7d251SAlgea Cao if (diff < best_diff || (diff == best_diff && 1866f5e7d251SAlgea Cao abs(n - ideal_n) < best_n_distance)) { 1867f5e7d251SAlgea Cao best_n = n; 1868f5e7d251SAlgea Cao best_diff = diff; 1869f5e7d251SAlgea Cao best_n_distance = abs(best_n - ideal_n); 1870f5e7d251SAlgea Cao } 1871f5e7d251SAlgea Cao 1872f5e7d251SAlgea Cao /* 1873f5e7d251SAlgea Cao * The best N already satisfy the audio math, and also be 1874f5e7d251SAlgea Cao * the closest value to ideal N, so just cut the loop. 1875f5e7d251SAlgea Cao */ 1876f5e7d251SAlgea Cao if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) 1877f5e7d251SAlgea Cao break; 1878f5e7d251SAlgea Cao } 1879f5e7d251SAlgea Cao 1880f5e7d251SAlgea Cao return best_n; 1881f5e7d251SAlgea Cao } 1882f5e7d251SAlgea Cao 1883f5e7d251SAlgea Cao static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk, 1884f5e7d251SAlgea Cao unsigned long sample_rate) 1885f5e7d251SAlgea Cao { 1886f5e7d251SAlgea Cao int n; 1887f5e7d251SAlgea Cao 1888f5e7d251SAlgea Cao n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); 1889f5e7d251SAlgea Cao if (n > 0) 1890f5e7d251SAlgea Cao return n; 1891f5e7d251SAlgea Cao 1892f5e7d251SAlgea Cao printf("Rate %lu missing; compute N dynamically\n", 1893f5e7d251SAlgea Cao pixel_clk); 1894f5e7d251SAlgea Cao 1895f5e7d251SAlgea Cao return hdmi_compute_n(hdmi, pixel_clk, sample_rate); 1896f5e7d251SAlgea Cao } 1897f5e7d251SAlgea Cao 1898f5e7d251SAlgea Cao static 1899f5e7d251SAlgea Cao void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, unsigned long pixel_clk, 1900f5e7d251SAlgea Cao unsigned int sample_rate) 1901f5e7d251SAlgea Cao { 1902f5e7d251SAlgea Cao unsigned long ftdms = pixel_clk; 1903f5e7d251SAlgea Cao unsigned int n, cts; 1904f5e7d251SAlgea Cao u64 tmp; 1905f5e7d251SAlgea Cao 1906f5e7d251SAlgea Cao n = hdmi_find_n(hdmi, pixel_clk, sample_rate); 1907f5e7d251SAlgea Cao 1908f5e7d251SAlgea Cao /* 1909f5e7d251SAlgea Cao * Compute the CTS value from the N value. Note that CTS and N 1910f5e7d251SAlgea Cao * can be up to 20 bits in total, so we need 64-bit math. Also 1911f5e7d251SAlgea Cao * note that our TDMS clock is not fully accurate; it is accurate 1912f5e7d251SAlgea Cao * to kHz. This can introduce an unnecessary remainder in the 1913f5e7d251SAlgea Cao * calculation below, so we don't try to warn about that. 1914f5e7d251SAlgea Cao */ 1915f5e7d251SAlgea Cao tmp = (u64)ftdms * n; 1916f5e7d251SAlgea Cao do_div(tmp, 128 * sample_rate); 1917f5e7d251SAlgea Cao cts = tmp; 1918f5e7d251SAlgea Cao 1919f5e7d251SAlgea Cao printf("%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", __func__, 1920f5e7d251SAlgea Cao sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, n, cts); 1921f5e7d251SAlgea Cao 1922f5e7d251SAlgea Cao hdmi->audio_n = n; 1923f5e7d251SAlgea Cao hdmi->audio_cts = cts; 1924f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0); 1925f5e7d251SAlgea Cao } 1926f5e7d251SAlgea Cao 1927f5e7d251SAlgea Cao static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) 1928f5e7d251SAlgea Cao { 19298e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, 1930f5e7d251SAlgea Cao hdmi->sample_rate); 1931f5e7d251SAlgea Cao } 1932f5e7d251SAlgea Cao 1933f5e7d251SAlgea Cao static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) 1934f5e7d251SAlgea Cao { 1935f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); 1936f5e7d251SAlgea Cao } 1937f5e7d251SAlgea Cao 1938f5e7d251SAlgea Cao void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) 1939f5e7d251SAlgea Cao { 1940f5e7d251SAlgea Cao hdmi->sample_rate = rate; 19418e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, 1942f5e7d251SAlgea Cao hdmi->sample_rate); 1943f5e7d251SAlgea Cao } 1944f5e7d251SAlgea Cao 19458e2bab3fSAlgea Cao static int dw_hdmi_hdcp_load_key(struct dw_hdmi *hdmi) 19468e2bab3fSAlgea Cao { 19478e2bab3fSAlgea Cao int i, j, ret, val; 19488e2bab3fSAlgea Cao struct hdcp_keys *hdcp_keys; 19498e2bab3fSAlgea Cao 19508e2bab3fSAlgea Cao val = sizeof(*hdcp_keys); 19518e2bab3fSAlgea Cao hdcp_keys = malloc(val); 19528e2bab3fSAlgea Cao if (!hdcp_keys) 19538e2bab3fSAlgea Cao return -ENOMEM; 19548e2bab3fSAlgea Cao 19558e2bab3fSAlgea Cao memset(hdcp_keys, 0, val); 19568e2bab3fSAlgea Cao 19578e2bab3fSAlgea Cao ret = vendor_storage_read(HDMI_HDCP1X_ID, hdcp_keys, val); 19588e2bab3fSAlgea Cao if (ret < val) { 19598e2bab3fSAlgea Cao printf("HDCP: read size %d\n", ret); 19608e2bab3fSAlgea Cao free(hdcp_keys); 19618e2bab3fSAlgea Cao return -EINVAL; 19628e2bab3fSAlgea Cao } 19638e2bab3fSAlgea Cao 19648e2bab3fSAlgea Cao if (hdcp_keys->KSV[0] == 0x00 && 19658e2bab3fSAlgea Cao hdcp_keys->KSV[1] == 0x00 && 19668e2bab3fSAlgea Cao hdcp_keys->KSV[2] == 0x00 && 19678e2bab3fSAlgea Cao hdcp_keys->KSV[3] == 0x00 && 19688e2bab3fSAlgea Cao hdcp_keys->KSV[4] == 0x00) { 19698e2bab3fSAlgea Cao printf("HDCP: Invalid hdcp key\n"); 19708e2bab3fSAlgea Cao free(hdcp_keys); 19718e2bab3fSAlgea Cao return -EINVAL; 19728e2bab3fSAlgea Cao } 19738e2bab3fSAlgea Cao 19748e2bab3fSAlgea Cao /* Disable decryption logic */ 19758e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_RMCTL); 19768e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */ 19778e2bab3fSAlgea Cao do { 19788e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 19798e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 19808e2bab3fSAlgea Cao 19818e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK6); 19828e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK5); 19838e2bab3fSAlgea Cao 19848e2bab3fSAlgea Cao /* The useful data in ksv should be 5 byte */ 19858e2bab3fSAlgea Cao for (i = 4; i >= 0; i--) 19868e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->KSV[i], HDMI_HDCPREG_DPK0 + i); 19878e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */ 19888e2bab3fSAlgea Cao do { 19898e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 19908e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 19918e2bab3fSAlgea Cao 19928e2bab3fSAlgea Cao /* Enable decryption logic */ 19938e2bab3fSAlgea Cao hdmi_writeb(hdmi, 1, HDMI_HDCPREG_RMCTL); 19948e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[0], HDMI_HDCPREG_SEED1); 19958e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[1], HDMI_HDCPREG_SEED0); 19968e2bab3fSAlgea Cao 19978e2bab3fSAlgea Cao /* Write encrypt device private key */ 19988e2bab3fSAlgea Cao for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) { 19998e2bab3fSAlgea Cao for (j = 6; j >= 0; j--) 20008e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->devicekey[i + j], 20018e2bab3fSAlgea Cao HDMI_HDCPREG_DPK0 + j); 20028e2bab3fSAlgea Cao do { 20038e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS); 20048e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0); 20058e2bab3fSAlgea Cao } 20068e2bab3fSAlgea Cao 20078e2bab3fSAlgea Cao free(hdcp_keys); 20088e2bab3fSAlgea Cao return 0; 20098e2bab3fSAlgea Cao } 20108e2bab3fSAlgea Cao 20118e2bab3fSAlgea Cao static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi, 20128e2bab3fSAlgea Cao const struct drm_display_mode *mode) 20138e2bab3fSAlgea Cao { 20148e2bab3fSAlgea Cao u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi; 20158e2bab3fSAlgea Cao 20168e2bab3fSAlgea Cao if (!hdmi->hdcp1x_enable) 20178e2bab3fSAlgea Cao return; 20188e2bab3fSAlgea Cao 20198e2bab3fSAlgea Cao /* Configure the video polarity */ 20208e2bab3fSAlgea Cao vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ? 20218e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH : 20228e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW; 20238e2bab3fSAlgea Cao hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ? 20248e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH : 20258e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW; 20268e2bab3fSAlgea Cao data_pol = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; 20278e2bab3fSAlgea Cao hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol, 20288e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_MASK | 20298e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_MASK | 20308e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_DATAENPOL_MASK, 20318e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG); 20328e2bab3fSAlgea Cao 20338e2bab3fSAlgea Cao /* Config the display mode */ 20348e2bab3fSAlgea Cao hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI : 20358e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_HDMIDVI_DVI; 20368e2bab3fSAlgea Cao hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK, 20378e2bab3fSAlgea Cao HDMI_A_HDCPCFG0); 20388e2bab3fSAlgea Cao 20398e2bab3fSAlgea Cao if (!(hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS) & 0x3f)) 20408e2bab3fSAlgea Cao dw_hdmi_hdcp_load_key(hdmi); 20418e2bab3fSAlgea Cao 20428e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE, 20438e2bab3fSAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK, 20448e2bab3fSAlgea Cao HDMI_FC_INVIDCONF); 20458e2bab3fSAlgea Cao 20468e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_A_HDCP22_MASK) { 20478e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_HDCP2_OVR_ENABLE | 20488e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_DISABLE, 20498e2bab3fSAlgea Cao HDMI_HDCP2_OVR_EN_MASK | 20508e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_MASK, 20518e2bab3fSAlgea Cao HDMI_HDCP2REG_CTRL); 20528e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MASK); 20538e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MUTE); 20548e2bab3fSAlgea Cao } 20558e2bab3fSAlgea Cao 20568e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x40, HDMI_A_OESSWCFG); 20578e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE | 20588e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE | 20598e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE, 20608e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK | 20618e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_MASK | 20628e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_MASK, HDMI_A_HDCPCFG0); 20638e2bab3fSAlgea Cao 20648e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE | 20658e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE, 20668e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK | 20678e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK, HDMI_A_HDCPCFG1); 20688e2bab3fSAlgea Cao 20698e2bab3fSAlgea Cao /* Reset HDCP Engine */ 20708e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_HDCPCLK_MASK) { 20718e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_SWRESET_ASSERT, 20728e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_SWRESET_MASK, HDMI_A_HDCPCFG1); 20738e2bab3fSAlgea Cao } 20748e2bab3fSAlgea Cao 20758e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK); 20768e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_ENABLE, 20778e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); 20788e2bab3fSAlgea Cao 20798e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_MC_CLKDIS_HDCPCLK_ENABLE, 20808e2bab3fSAlgea Cao HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS); 20818e2bab3fSAlgea Cao 20828e2bab3fSAlgea Cao printf("%s success\n", __func__); 20838e2bab3fSAlgea Cao } 20848e2bab3fSAlgea Cao 2085f5e7d251SAlgea Cao static int dw_hdmi_setup(struct dw_hdmi *hdmi, 20860594ce39SZhang Yubing struct rockchip_connector *conn, 20878e2bab3fSAlgea Cao struct drm_display_mode *mode, 20888e2bab3fSAlgea Cao struct display_state *state) 2089f5e7d251SAlgea Cao { 2090f5e7d251SAlgea Cao int ret; 2091f5e7d251SAlgea Cao void *data = hdmi->plat_data->phy_data; 2092f5e7d251SAlgea Cao 2093f5e7d251SAlgea Cao hdmi_disable_overflow_interrupts(hdmi); 2094f5e7d251SAlgea Cao if (!hdmi->vic) 2095f5e7d251SAlgea Cao printf("Non-CEA mode used in HDMI\n"); 2096f5e7d251SAlgea Cao else 2097f5e7d251SAlgea Cao printf("CEA mode used vic=%d\n", hdmi->vic); 2098f5e7d251SAlgea Cao 2099f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_out_encoding) 2100f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = 2101f5e7d251SAlgea Cao hdmi->plat_data->get_enc_out_encoding(data); 21025ccad8f6SAlgea Cao else if (hdmi->vic == 6 || hdmi->vic == 7 || 21035ccad8f6SAlgea Cao hdmi->vic == 21 || hdmi->vic == 22 || 21045ccad8f6SAlgea Cao hdmi->vic == 2 || hdmi->vic == 3 || 21055ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18) 2106f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; 2107f5e7d251SAlgea Cao else 2108f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; 2109f5e7d251SAlgea Cao 2110f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_DBLCLK) { 2111f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; 2112f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; 2113f5e7d251SAlgea Cao } else { 2114f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; 2115f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; 2116f5e7d251SAlgea Cao } 2117f5e7d251SAlgea Cao 2118f5e7d251SAlgea Cao /* TOFIX: Get input encoding from plat data or fallback to none */ 2119f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_in_encoding) 2120f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = 2121f5e7d251SAlgea Cao hdmi->plat_data->get_enc_in_encoding(data); 2122f5e7d251SAlgea Cao else if (hdmi->plat_data->input_bus_encoding) 2123f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = 2124f5e7d251SAlgea Cao hdmi->plat_data->input_bus_encoding; 2125f5e7d251SAlgea Cao else 2126f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; 2127b5016cf2SAlgea Cao 2128b5016cf2SAlgea Cao if (hdmi->plat_data->get_quant_range) 2129b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range = 2130b5016cf2SAlgea Cao hdmi->plat_data->get_quant_range(data); 2131b5016cf2SAlgea Cao else 2132b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; 2133b5016cf2SAlgea Cao 2134f5e7d251SAlgea Cao /* 2135f5e7d251SAlgea Cao * According to the dw-hdmi specification 6.4.2 2136f5e7d251SAlgea Cao * vp_pr_cd[3:0]: 2137f5e7d251SAlgea Cao * 0000b: No pixel repetition (pixel sent only once) 2138f5e7d251SAlgea Cao * 0001b: Pixel sent two times (pixel repeated once) 2139f5e7d251SAlgea Cao */ 2140f5e7d251SAlgea Cao hdmi->hdmi_data.pix_repet_factor = 2141f5e7d251SAlgea Cao (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; 2142f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mdataenablepolarity = true; 2143f5e7d251SAlgea Cao 2144f5e7d251SAlgea Cao /* HDMI Initialization Step B.1 */ 2145f5e7d251SAlgea Cao hdmi_av_composer(hdmi, mode); 2146f5e7d251SAlgea Cao 2147f5e7d251SAlgea Cao /* HDMI Initialization Step B.2 */ 21480594ce39SZhang Yubing ret = hdmi->phy.ops->init(conn, hdmi, state); 2149f5e7d251SAlgea Cao if (ret) 2150f5e7d251SAlgea Cao return ret; 2151f5e7d251SAlgea Cao hdmi->phy.enabled = true; 2152f5e7d251SAlgea Cao 2153f5e7d251SAlgea Cao /* HDMI Initializateion Step B.3 */ 2154f5e7d251SAlgea Cao dw_hdmi_enable_video_path(hdmi); 2155f5e7d251SAlgea Cao 2156f5e7d251SAlgea Cao /* HDMI Initialization Step E - Configure audio */ 2157f5e7d251SAlgea Cao if (hdmi->sink_has_audio) { 2158f5e7d251SAlgea Cao printf("sink has audio support\n"); 2159f5e7d251SAlgea Cao hdmi_clk_regenerator_update_pixel_clock(hdmi); 2160f5e7d251SAlgea Cao hdmi_enable_audio_clk(hdmi); 2161f5e7d251SAlgea Cao } 2162f5e7d251SAlgea Cao 2163f5e7d251SAlgea Cao /* not for DVI mode */ 2164f5e7d251SAlgea Cao if (hdmi->sink_is_hdmi) { 2165f5e7d251SAlgea Cao /* HDMI Initialization Step F - Configure AVI InfoFrame */ 2166f5e7d251SAlgea Cao hdmi_config_AVI(hdmi, mode); 2167f5e7d251SAlgea Cao hdmi_config_vendor_specific_infoframe(hdmi, mode); 2168b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_HDMI, 2169b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK, 2170b327b539SShunqing Chen HDMI_A_HDCPCFG0); 2171f5e7d251SAlgea Cao } else { 2172b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_DVI, 2173b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK, 2174b327b539SShunqing Chen HDMI_A_HDCPCFG0); 2175f5e7d251SAlgea Cao printf("%s DVI mode\n", __func__); 2176f5e7d251SAlgea Cao } 2177f5e7d251SAlgea Cao 2178f5e7d251SAlgea Cao hdmi_video_packetize(hdmi); 2179f5e7d251SAlgea Cao hdmi_video_csc(hdmi); 2180f5e7d251SAlgea Cao hdmi_video_sample(hdmi); 21818e2bab3fSAlgea Cao hdmi_tx_hdcp_config(hdmi, mode); 2182f5e7d251SAlgea Cao dw_hdmi_clear_overflow(hdmi); 2183f5e7d251SAlgea Cao 2184f5e7d251SAlgea Cao return 0; 2185f5e7d251SAlgea Cao } 2186f5e7d251SAlgea Cao 21878e2bab3fSAlgea Cao int dw_hdmi_detect_hotplug(struct dw_hdmi *hdmi, 21888e2bab3fSAlgea Cao struct display_state *state) 2189f5e7d251SAlgea Cao { 21908e2bab3fSAlgea Cao return hdmi->phy.ops->read_hpd(hdmi, state); 2191f5e7d251SAlgea Cao } 2192f5e7d251SAlgea Cao 2193f5e7d251SAlgea Cao static int dw_hdmi_set_reg_wr(struct dw_hdmi *hdmi) 2194f5e7d251SAlgea Cao { 2195f5e7d251SAlgea Cao switch (hdmi->io_width) { 2196f5e7d251SAlgea Cao case 4: 2197f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writel; 2198f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readl; 2199f5e7d251SAlgea Cao break; 2200f5e7d251SAlgea Cao case 1: 2201f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writeb; 2202f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readb; 2203f5e7d251SAlgea Cao break; 2204f5e7d251SAlgea Cao default: 2205f5e7d251SAlgea Cao printf("reg-io-width must be 1 or 4\n"); 2206f5e7d251SAlgea Cao return -EINVAL; 2207f5e7d251SAlgea Cao } 2208f5e7d251SAlgea Cao 2209f5e7d251SAlgea Cao return 0; 2210f5e7d251SAlgea Cao } 2211f5e7d251SAlgea Cao 2212f5e7d251SAlgea Cao static void initialize_hdmi_mutes(struct dw_hdmi *hdmi) 2213f5e7d251SAlgea Cao { 2214f5e7d251SAlgea Cao /*mute unnecessary interrupt, only enable hpd */ 22155ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0); 22165ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1); 22175ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2); 2218f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0); 2219f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1); 2220f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2); 2221f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0); 2222f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xfe, HDMI_IH_MUTE_PHY_STAT0); 2223f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0); 2224f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0); 2225f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0); 2226f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0); 2227f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0); 2228f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xf1, HDMI_PHY_MASK0); 2229f5e7d251SAlgea Cao 2230f5e7d251SAlgea Cao /*Force output black*/ 2231f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS2); 2232f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS1); 2233f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS0); 2234f5e7d251SAlgea Cao } 2235f5e7d251SAlgea Cao 2236f5e7d251SAlgea Cao static void dw_hdmi_dev_init(struct dw_hdmi *hdmi) 2237f5e7d251SAlgea Cao { 2238f5e7d251SAlgea Cao hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) 2239f5e7d251SAlgea Cao | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); 2240f5e7d251SAlgea Cao 2241f5e7d251SAlgea Cao initialize_hdmi_mutes(hdmi); 2242f5e7d251SAlgea Cao } 2243f5e7d251SAlgea Cao 22448e2bab3fSAlgea Cao static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi) 2245f5e7d251SAlgea Cao { 22468e2bab3fSAlgea Cao unsigned long low_ns, high_ns; 22478e2bab3fSAlgea Cao unsigned long div_low, div_high; 2248f5e7d251SAlgea Cao 22498e2bab3fSAlgea Cao /* Standard-mode */ 22508e2bab3fSAlgea Cao if (hdmi->i2c->scl_high_ns < 4000) 22518e2bab3fSAlgea Cao high_ns = 4708; 2252f5e7d251SAlgea Cao else 22538e2bab3fSAlgea Cao high_ns = hdmi->i2c->scl_high_ns; 2254f5e7d251SAlgea Cao 22558e2bab3fSAlgea Cao if (hdmi->i2c->scl_low_ns < 4700) 22568e2bab3fSAlgea Cao low_ns = 4916; 22578e2bab3fSAlgea Cao else 22588e2bab3fSAlgea Cao low_ns = hdmi->i2c->scl_low_ns; 22598e2bab3fSAlgea Cao 22608e2bab3fSAlgea Cao div_low = (24000 * low_ns) / 1000000; 22618e2bab3fSAlgea Cao if ((24000 * low_ns) % 1000000) 22628e2bab3fSAlgea Cao div_low++; 22638e2bab3fSAlgea Cao 22648e2bab3fSAlgea Cao div_high = (24000 * high_ns) / 1000000; 22658e2bab3fSAlgea Cao if ((24000 * high_ns) % 1000000) 22668e2bab3fSAlgea Cao div_high++; 22678e2bab3fSAlgea Cao 22688e2bab3fSAlgea Cao /* Maximum divider supported by hw is 0xffff */ 22698e2bab3fSAlgea Cao if (div_low > 0xffff) 22708e2bab3fSAlgea Cao div_low = 0xffff; 22718e2bab3fSAlgea Cao 22728e2bab3fSAlgea Cao if (div_high > 0xffff) 22738e2bab3fSAlgea Cao div_high = 0xffff; 22748e2bab3fSAlgea Cao 22758e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR); 22768e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_high >> 8) & 0xff, 22778e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_HCNT_1_ADDR); 22788e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR); 22798e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_low >> 8) & 0xff, 22808e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_LCNT_1_ADDR); 2281f5e7d251SAlgea Cao } 2282f5e7d251SAlgea Cao 22838e2bab3fSAlgea Cao static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) 2284f5e7d251SAlgea Cao { 22858e2bab3fSAlgea Cao /* Software reset */ 22868e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); 2287f5e7d251SAlgea Cao 22888e2bab3fSAlgea Cao /* Set Standard Mode speed */ 22898e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE, 22908e2bab3fSAlgea Cao HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV); 2291f5e7d251SAlgea Cao 22928e2bab3fSAlgea Cao /* Set done, not acknowledged and arbitration interrupt polarities */ 22938e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT); 22948e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL, 22958e2bab3fSAlgea Cao HDMI_I2CM_CTLINT); 2296f5e7d251SAlgea Cao 22978e2bab3fSAlgea Cao /* Clear DONE and ERROR interrupts */ 22988e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 22998e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0); 2300f5e7d251SAlgea Cao 23018e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */ 23028e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, 23038e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0); 2304f5e7d251SAlgea Cao 23058e2bab3fSAlgea Cao /* set SDA high level holding time */ 23068e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD); 2307f5e7d251SAlgea Cao 23088e2bab3fSAlgea Cao dw_hdmi_i2c_set_divs(hdmi); 2309f5e7d251SAlgea Cao } 2310f5e7d251SAlgea Cao 2311f5e7d251SAlgea Cao void dw_hdmi_audio_enable(struct dw_hdmi *hdmi) 2312f5e7d251SAlgea Cao { 2313f5e7d251SAlgea Cao hdmi->audio_enable = true; 2314f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); 2315f5e7d251SAlgea Cao } 2316f5e7d251SAlgea Cao 2317f5e7d251SAlgea Cao void dw_hdmi_audio_disable(struct dw_hdmi *hdmi) 2318f5e7d251SAlgea Cao { 2319f5e7d251SAlgea Cao hdmi->audio_enable = false; 2320f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); 2321f5e7d251SAlgea Cao } 2322f5e7d251SAlgea Cao 23230594ce39SZhang Yubing int rockchip_dw_hdmi_init(struct rockchip_connector *conn, struct display_state *state) 232458c17f51SSandy Huang { 232558c17f51SSandy Huang struct connector_state *conn_state = &state->conn_state; 23260594ce39SZhang Yubing const struct dw_hdmi_plat_data *pdata = 23270594ce39SZhang Yubing (const struct dw_hdmi_plat_data *)dev_get_driver_data(conn->dev); 2328f5e7d251SAlgea Cao struct crtc_state *crtc_state = &state->crtc_state; 2329f5e7d251SAlgea Cao struct dw_hdmi *hdmi; 2330f5e7d251SAlgea Cao struct drm_display_mode *mode_buf; 23310594ce39SZhang Yubing ofnode hdmi_node = conn->dev->node; 2332f5e7d251SAlgea Cao u32 val; 23331f71919fSShunqing Chen struct device_node *ddc_node; 2334cb24dc0eSAlgea Cao int ret; 2335f5e7d251SAlgea Cao 2336f5e7d251SAlgea Cao hdmi = malloc(sizeof(struct dw_hdmi)); 2337f5e7d251SAlgea Cao if (!hdmi) 2338f5e7d251SAlgea Cao return -ENOMEM; 2339f5e7d251SAlgea Cao 2340f5e7d251SAlgea Cao memset(hdmi, 0, sizeof(struct dw_hdmi)); 2341f5e7d251SAlgea Cao mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode)); 2342f5e7d251SAlgea Cao if (!mode_buf) 2343f5e7d251SAlgea Cao return -ENOMEM; 2344cb17ca6cSSandy Huang hdmi->id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi"); 2345cb17ca6cSSandy Huang if (hdmi->id < 0) 2346cb17ca6cSSandy Huang hdmi->id = 0; 2347cb17ca6cSSandy Huang conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id); 23488e2bab3fSAlgea Cao 2349f5e7d251SAlgea Cao memset(mode_buf, 0, MODE_LEN * sizeof(struct drm_display_mode)); 2350f5e7d251SAlgea Cao 23517ff748e1SAlgea Cao hdmi->dev_type = pdata->dev_type; 23527ff748e1SAlgea Cao hdmi->plat_data = pdata; 23537ff748e1SAlgea Cao 23540594ce39SZhang Yubing hdmi->regs = dev_read_addr_ptr(conn->dev); 2355e2bce6e4SKever Yang hdmi->io_width = ofnode_read_s32_default(hdmi_node, "reg-io-width", -1); 23568e2bab3fSAlgea Cao 23578e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "scramble-low-rates")) 23588e2bab3fSAlgea Cao hdmi->scramble_low_rates = true; 23598e2bab3fSAlgea Cao 23608e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "hdcp1x-enable")) 23618e2bab3fSAlgea Cao hdmi->hdcp1x_enable = true; 23628e2bab3fSAlgea Cao else 23638e2bab3fSAlgea Cao hdmi->hdcp1x_enable = false; 23648e2bab3fSAlgea Cao 236591e56900SLei Chen if (ofnode_read_bool(hdmi_node, "force_output_bus_format_RGB") || 236691e56900SLei Chen ofnode_read_bool(hdmi_node, "unsupported-yuv-input")) 236791e56900SLei Chen hdmi->output_bus_format_rgb = true; 236891e56900SLei Chen else 236991e56900SLei Chen hdmi->output_bus_format_rgb = false; 237091e56900SLei Chen 2371d5869560SAlgea Cao ret = dev_read_size(conn->dev, "rockchip,phy-table"); 2372d5869560SAlgea Cao if (ret > 0 && hdmi->plat_data->phy_config) { 2373d5869560SAlgea Cao u32 phy_config[ret / 4]; 23747ff748e1SAlgea Cao int i; 23757ff748e1SAlgea Cao 2376d5869560SAlgea Cao dev_read_u32_array(conn->dev, "rockchip,phy-table", phy_config, ret / 4); 23777ff748e1SAlgea Cao 2378d5869560SAlgea Cao for (i = 0; i < ret / 16; i++) { 23797ff748e1SAlgea Cao if (phy_config[i * 4] != 0) 23807ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].mpixelclock = (u64)phy_config[i * 4]; 23817ff748e1SAlgea Cao else 23827ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].mpixelclock = ~0UL; 23837ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].sym_ctr = (u16)phy_config[i * 4 + 1]; 23847ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].term = (u16)phy_config[i * 4 + 2]; 23857ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].vlev_ctr = (u16)phy_config[i * 4 + 3]; 23867ff748e1SAlgea Cao } 23877ff748e1SAlgea Cao } 23887ff748e1SAlgea Cao 23891f71919fSShunqing Chen ddc_node = of_parse_phandle(ofnode_to_np(hdmi_node), "ddc-i2c-bus", 0); 23901f71919fSShunqing Chen if (ddc_node) { 23911f71919fSShunqing Chen uclass_get_device_by_ofnode(UCLASS_I2C, np_to_ofnode(ddc_node), 23921f71919fSShunqing Chen &hdmi->adap.i2c_bus); 23931f71919fSShunqing Chen if (hdmi->adap.i2c_bus) 23941f71919fSShunqing Chen hdmi->adap.ops = i2c_get_ops(hdmi->adap.i2c_bus); 23951f71919fSShunqing Chen } 23961f71919fSShunqing Chen 2397f5e7d251SAlgea Cao hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 2398f5e7d251SAlgea Cao if (hdmi->grf <= 0) { 2399f5e7d251SAlgea Cao printf("%s: Get syscon grf failed (ret=%p)\n", 2400f5e7d251SAlgea Cao __func__, hdmi->grf); 2401f5e7d251SAlgea Cao return -ENXIO; 2402f5e7d251SAlgea Cao } 2403f5e7d251SAlgea Cao 2404cb24dc0eSAlgea Cao hdmi->gpio_base = (void *)dev_read_addr_index(conn->dev, 1); 2405cb24dc0eSAlgea Cao if (!hdmi->gpio_base) 2406cb24dc0eSAlgea Cao return -ENODEV; 2407cb24dc0eSAlgea Cao 2408cb24dc0eSAlgea Cao ret = gpio_request_by_name(conn->dev, "hpd-gpios", 0, 2409cb24dc0eSAlgea Cao &hdmi->hpd_gpiod, GPIOD_IS_IN); 2410cb24dc0eSAlgea Cao if (ret && ret != -ENOENT) { 2411cb24dc0eSAlgea Cao printf("%s: Cannot get HPD GPIO: %d\n", __func__, ret); 2412cb24dc0eSAlgea Cao return ret; 2413cb24dc0eSAlgea Cao } 2414cb24dc0eSAlgea Cao 2415f5e7d251SAlgea Cao dw_hdmi_set_reg_wr(hdmi); 2416f5e7d251SAlgea Cao 24178e2bab3fSAlgea Cao if (pdata->grf_vop_sel_reg) { 2418f5e7d251SAlgea Cao if (crtc_state->crtc_id) 2419f5e7d251SAlgea Cao val = ((1 << pdata->vop_sel_bit) | 2420f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit))); 2421f5e7d251SAlgea Cao else 2422f5e7d251SAlgea Cao val = ((0 << pdata->vop_sel_bit) | 2423f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit))); 2424f5e7d251SAlgea Cao writel(val, hdmi->grf + pdata->grf_vop_sel_reg); 24258e2bab3fSAlgea Cao } 2426f5e7d251SAlgea Cao 24278e2bab3fSAlgea Cao hdmi->i2c = malloc(sizeof(struct dw_hdmi_i2c)); 24288e2bab3fSAlgea Cao if (!hdmi->i2c) 24298e2bab3fSAlgea Cao return -ENOMEM; 24308e2bab3fSAlgea Cao hdmi->adap.ddc_xfer = dw_hdmi_i2c_xfer; 24318e2bab3fSAlgea Cao 24328e2bab3fSAlgea Cao /* 24338e2bab3fSAlgea Cao * Read high and low time from device tree. If not available use 24348e2bab3fSAlgea Cao * the default timing scl clock rate is about 99.6KHz. 24358e2bab3fSAlgea Cao */ 24368e2bab3fSAlgea Cao hdmi->i2c->scl_high_ns = 24378e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node, 243819c2faf2SAlgea Cao "ddc-i2c-scl-high-time-ns", 4708); 24398e2bab3fSAlgea Cao hdmi->i2c->scl_low_ns = 24408e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node, 244119c2faf2SAlgea Cao "ddc-i2c-scl-low-time-ns", 4916); 24428e2bab3fSAlgea Cao 24438e2bab3fSAlgea Cao dw_hdmi_i2c_init(hdmi); 24445ccad8f6SAlgea Cao conn_state->output_if |= VOP_OUTPUT_IF_HDMI0; 2445f5e7d251SAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; 2446f5e7d251SAlgea Cao 2447f5e7d251SAlgea Cao hdmi->edid_data.mode_buf = mode_buf; 2448f5e7d251SAlgea Cao hdmi->sample_rate = 48000; 2449f5e7d251SAlgea Cao 24500594ce39SZhang Yubing conn->data = hdmi; 2451cb24dc0eSAlgea Cao dw_hdmi_set_iomux(hdmi->grf, hdmi->gpio_base, 2452cb24dc0eSAlgea Cao &hdmi->hpd_gpiod, hdmi->dev_type); 2453f5e7d251SAlgea Cao dw_hdmi_detect_phy(hdmi); 2454f5e7d251SAlgea Cao dw_hdmi_dev_init(hdmi); 2455f5e7d251SAlgea Cao 2456f5e7d251SAlgea Cao return 0; 2457f5e7d251SAlgea Cao } 2458f5e7d251SAlgea Cao 24590594ce39SZhang Yubing void rockchip_dw_hdmi_deinit(struct rockchip_connector *conn, struct display_state *state) 2460f5e7d251SAlgea Cao { 24610594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 2462f5e7d251SAlgea Cao 24638e2bab3fSAlgea Cao if (hdmi->i2c) 24648e2bab3fSAlgea Cao free(hdmi->i2c); 2465f5e7d251SAlgea Cao if (hdmi->edid_data.mode_buf) 2466f5e7d251SAlgea Cao free(hdmi->edid_data.mode_buf); 2467f5e7d251SAlgea Cao if (hdmi) 2468f5e7d251SAlgea Cao free(hdmi); 2469f5e7d251SAlgea Cao } 2470f5e7d251SAlgea Cao 24710594ce39SZhang Yubing int rockchip_dw_hdmi_prepare(struct rockchip_connector *conn, struct display_state *state) 2472f5e7d251SAlgea Cao { 2473f5e7d251SAlgea Cao return 0; 2474f5e7d251SAlgea Cao } 2475f5e7d251SAlgea Cao 24760594ce39SZhang Yubing int rockchip_dw_hdmi_enable(struct rockchip_connector *conn, struct display_state *state) 2477f5e7d251SAlgea Cao { 2478f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2479f5e7d251SAlgea Cao struct drm_display_mode *mode = &conn_state->mode; 24800594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 2481f5e7d251SAlgea Cao 2482f5e7d251SAlgea Cao if (!hdmi) 2483f5e7d251SAlgea Cao return -EFAULT; 2484f5e7d251SAlgea Cao 24858e2bab3fSAlgea Cao /* Store the display mode for plugin/DKMS poweron events */ 24868e2bab3fSAlgea Cao memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); 24878e2bab3fSAlgea Cao 24880594ce39SZhang Yubing dw_hdmi_setup(hdmi, conn, mode, state); 2489f5e7d251SAlgea Cao 2490f5e7d251SAlgea Cao return 0; 2491f5e7d251SAlgea Cao } 2492f5e7d251SAlgea Cao 24930594ce39SZhang Yubing int rockchip_dw_hdmi_disable(struct rockchip_connector *conn, struct display_state *state) 2494f5e7d251SAlgea Cao { 24950594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 2496f5e7d251SAlgea Cao 24970594ce39SZhang Yubing dw_hdmi_disable(conn, hdmi, state); 2498f5e7d251SAlgea Cao return 0; 2499f5e7d251SAlgea Cao } 2500f5e7d251SAlgea Cao 25010594ce39SZhang Yubing int rockchip_dw_hdmi_get_timing(struct rockchip_connector *conn, struct display_state *state) 2502f5e7d251SAlgea Cao { 250325471c69SAlgea Cao int ret, i, vic; 2504f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 2505f5e7d251SAlgea Cao struct drm_display_mode *mode = &conn_state->mode; 25060594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 25078e2bab3fSAlgea Cao struct edid *edid = (struct edid *)conn_state->edid; 25088e2bab3fSAlgea Cao unsigned int bus_format; 25095ccad8f6SAlgea Cao unsigned long enc_out_encoding; 25108e2bab3fSAlgea Cao struct overscan *overscan = &conn_state->overscan; 25118e2bab3fSAlgea Cao const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19}; 2512f5e7d251SAlgea Cao 2513f5e7d251SAlgea Cao if (!hdmi) 2514f5e7d251SAlgea Cao return -EFAULT; 2515f5e7d251SAlgea Cao 25168e2bab3fSAlgea Cao ret = drm_do_get_edid(&hdmi->adap, conn_state->edid); 25175ccad8f6SAlgea Cao 25188e2bab3fSAlgea Cao if (!ret) { 2519f5e7d251SAlgea Cao hdmi->sink_is_hdmi = 25208e2bab3fSAlgea Cao drm_detect_hdmi_monitor(edid); 25218e2bab3fSAlgea Cao hdmi->sink_has_audio = drm_detect_monitor_audio(edid); 25228e2bab3fSAlgea Cao ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid); 25238e2bab3fSAlgea Cao } 2524c613b7eaSAlgea Cao if (ret < 0) { 25258e2bab3fSAlgea Cao hdmi->sink_is_hdmi = true; 25268e2bab3fSAlgea Cao hdmi->sink_has_audio = true; 25278e2bab3fSAlgea Cao do_cea_modes(&hdmi->edid_data, def_modes_vic, 25288e2bab3fSAlgea Cao sizeof(def_modes_vic)); 252994b0daefSAlgea Cao hdmi->edid_data.mode_buf[0].type |= DRM_MODE_TYPE_PREFERRED; 25308e2bab3fSAlgea Cao hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0]; 25318e2bab3fSAlgea Cao printf("failed to get edid\n"); 25328e2bab3fSAlgea Cao } 25338e2bab3fSAlgea Cao drm_rk_filter_whitelist(&hdmi->edid_data); 25348e2bab3fSAlgea Cao if (hdmi->phy.ops->mode_valid) 25350594ce39SZhang Yubing hdmi->phy.ops->mode_valid(conn, hdmi, state); 2536cf53642aSSandy Huang drm_mode_max_resolution_filter(&hdmi->edid_data, 2537cf53642aSSandy Huang &state->crtc_state.max_output); 25388e2bab3fSAlgea Cao if (!drm_mode_prune_invalid(&hdmi->edid_data)) { 25398e2bab3fSAlgea Cao printf("can't find valid hdmi mode\n"); 25408e2bab3fSAlgea Cao return -EINVAL; 25418e2bab3fSAlgea Cao } 25428e2bab3fSAlgea Cao 254325471c69SAlgea Cao for (i = 0; i < hdmi->edid_data.modes; i++) { 25448e2bab3fSAlgea Cao hdmi->edid_data.mode_buf[i].vrefresh = 25458e2bab3fSAlgea Cao drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]); 25468e2bab3fSAlgea Cao 254725471c69SAlgea Cao vic = drm_match_cea_mode(&hdmi->edid_data.mode_buf[i]); 254825471c69SAlgea Cao if (hdmi->edid_data.mode_buf[i].picture_aspect_ratio == HDMI_PICTURE_ASPECT_NONE) { 254925471c69SAlgea Cao if (vic >= 93 && vic <= 95) 255025471c69SAlgea Cao hdmi->edid_data.mode_buf[i].picture_aspect_ratio = 255125471c69SAlgea Cao HDMI_PICTURE_ASPECT_16_9; 255225471c69SAlgea Cao else if (vic == 98) 255325471c69SAlgea Cao hdmi->edid_data.mode_buf[i].picture_aspect_ratio = 255425471c69SAlgea Cao HDMI_PICTURE_ASPECT_256_135; 255525471c69SAlgea Cao } 255625471c69SAlgea Cao } 255725471c69SAlgea Cao 25588e2bab3fSAlgea Cao drm_mode_sort(&hdmi->edid_data); 25595ccb1b20SAlgea Cao drm_rk_selete_output(&hdmi->edid_data, conn_state, &bus_format, 256091e56900SLei Chen overscan, hdmi->dev_type, hdmi->output_bus_format_rgb); 25618e2bab3fSAlgea Cao 2562f5e7d251SAlgea Cao *mode = *hdmi->edid_data.preferred_mode; 2563f5e7d251SAlgea Cao hdmi->vic = drm_match_cea_mode(mode); 2564f5e7d251SAlgea Cao 25658e2bab3fSAlgea Cao printf("mode:%dx%d\n", mode->hdisplay, mode->vdisplay); 25662bfb6166SSandy Huang if (state->force_output) 25672bfb6166SSandy Huang bus_format = state->force_bus_format; 25688e2bab3fSAlgea Cao conn_state->bus_format = bus_format; 25698e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = bus_format; 25708e2bab3fSAlgea Cao hdmi->hdmi_data.enc_out_bus_format = bus_format; 2571f5e7d251SAlgea Cao 25728e2bab3fSAlgea Cao switch (bus_format) { 25738e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20: 25748e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV10_1X30; 25758e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = 25768e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV10_1X30; 25778e2bab3fSAlgea Cao break; 25788e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16: 25798e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV8_1X24; 25808e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = 25818e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV8_1X24; 25828e2bab3fSAlgea Cao break; 25838e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 25848e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 25858e2bab3fSAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV420; 25868e2bab3fSAlgea Cao break; 25878e2bab3fSAlgea Cao } 2588f5e7d251SAlgea Cao 25895ccad8f6SAlgea Cao if (hdmi->vic == 6 || hdmi->vic == 7 || hdmi->vic == 21 || 25905ccad8f6SAlgea Cao hdmi->vic == 22 || hdmi->vic == 2 || hdmi->vic == 3 || 25915ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18) 25925ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_601; 25935ccad8f6SAlgea Cao else 25945ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_709; 25955ccad8f6SAlgea Cao 25965ccad8f6SAlgea Cao if (enc_out_encoding == V4L2_YCBCR_ENC_BT2020) 25975ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_BT2020; 25985ccad8f6SAlgea Cao else if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 || 25995ccad8f6SAlgea Cao bus_format == MEDIA_BUS_FMT_RGB101010_1X30) 26005ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_DEFAULT; 26015ccad8f6SAlgea Cao else if (enc_out_encoding == V4L2_YCBCR_ENC_709) 26025ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_REC709; 26035ccad8f6SAlgea Cao else 26045ccad8f6SAlgea Cao conn_state->color_space = V4L2_COLORSPACE_SMPTE170M; 26055ccad8f6SAlgea Cao 2606f5e7d251SAlgea Cao return 0; 2607f5e7d251SAlgea Cao } 2608f5e7d251SAlgea Cao 26090594ce39SZhang Yubing int rockchip_dw_hdmi_detect(struct rockchip_connector *conn, struct display_state *state) 2610f5e7d251SAlgea Cao { 2611f5e7d251SAlgea Cao int ret; 26120594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 2613f5e7d251SAlgea Cao 2614f5e7d251SAlgea Cao if (!hdmi) 2615f5e7d251SAlgea Cao return -EFAULT; 2616f5e7d251SAlgea Cao 26178e2bab3fSAlgea Cao ret = dw_hdmi_detect_hotplug(hdmi, state); 2618f5e7d251SAlgea Cao 2619f5e7d251SAlgea Cao return ret; 2620f5e7d251SAlgea Cao } 2621f5e7d251SAlgea Cao 26220594ce39SZhang Yubing int rockchip_dw_hdmi_get_edid(struct rockchip_connector *conn, struct display_state *state) 2623f5e7d251SAlgea Cao { 2624f5e7d251SAlgea Cao int ret; 2625f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state; 26260594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data; 2627f5e7d251SAlgea Cao 26288e2bab3fSAlgea Cao ret = drm_do_get_edid(&hdmi->adap, conn_state->edid); 2629f5e7d251SAlgea Cao 2630f5e7d251SAlgea Cao return ret; 2631f5e7d251SAlgea Cao } 2632f5e7d251SAlgea Cao 26330594ce39SZhang Yubing int inno_dw_hdmi_phy_init(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data) 26348e2bab3fSAlgea Cao { 26358e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data; 26368e2bab3fSAlgea Cao struct connector_state *conn_state = &state->conn_state; 26378e2bab3fSAlgea Cao u32 color_depth, bus_width; 26388e2bab3fSAlgea Cao 26398e2bab3fSAlgea Cao color_depth = 26408e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); 26418e2bab3fSAlgea Cao 26428e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) 26438e2bab3fSAlgea Cao bus_width = color_depth / 2; 26448e2bab3fSAlgea Cao else if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 26458e2bab3fSAlgea Cao bus_width = color_depth; 26468e2bab3fSAlgea Cao else 26478e2bab3fSAlgea Cao bus_width = 8; 26480594ce39SZhang Yubing rockchip_phy_set_bus_width(conn->phy, bus_width); 26490594ce39SZhang Yubing rockchip_phy_set_pll(conn->phy, 265015081c50SWyon Bi conn_state->mode.crtc_clock * 1000); 26518e2bab3fSAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported) 26528e2bab3fSAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi); 26530594ce39SZhang Yubing rockchip_phy_power_on(conn->phy); 26548e2bab3fSAlgea Cao 26558e2bab3fSAlgea Cao return 0; 26568e2bab3fSAlgea Cao } 26578e2bab3fSAlgea Cao 26580594ce39SZhang Yubing void inno_dw_hdmi_phy_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data) 26598e2bab3fSAlgea Cao { 26608e2bab3fSAlgea Cao } 26618e2bab3fSAlgea Cao 26628e2bab3fSAlgea Cao enum drm_connector_status 26638e2bab3fSAlgea Cao inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data) 26648e2bab3fSAlgea Cao { 26658e2bab3fSAlgea Cao enum drm_connector_status status; 26668e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data; 26678e2bab3fSAlgea Cao 26688e2bab3fSAlgea Cao status = dw_hdmi_phy_read_hpd(hdmi, state); 26698e2bab3fSAlgea Cao 26708e2bab3fSAlgea Cao if (hdmi->dev_type == RK3328_HDMI) { 26718e2bab3fSAlgea Cao if (status == connector_status_connected) 26728e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 1); 26738e2bab3fSAlgea Cao else 26748e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 0); 26758e2bab3fSAlgea Cao } 26768e2bab3fSAlgea Cao 26778e2bab3fSAlgea Cao return status; 26788e2bab3fSAlgea Cao } 26798e2bab3fSAlgea Cao 26800594ce39SZhang Yubing void inno_dw_hdmi_mode_valid(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data) 26818e2bab3fSAlgea Cao { 26828e2bab3fSAlgea Cao struct hdmi_edid_data *edid_data = &hdmi->edid_data; 26838e2bab3fSAlgea Cao unsigned long rate; 26848e2bab3fSAlgea Cao int i, ret; 26858e2bab3fSAlgea Cao struct drm_display_mode *mode_buf = edid_data->mode_buf; 26868e2bab3fSAlgea Cao 26878e2bab3fSAlgea Cao for (i = 0; i < edid_data->modes; i++) { 26888e2bab3fSAlgea Cao if (edid_data->mode_buf[i].invalid) 26898e2bab3fSAlgea Cao continue; 26908e2bab3fSAlgea Cao if (edid_data->mode_buf[i].flags & DRM_MODE_FLAG_DBLCLK) 26918e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000 * 2; 26928e2bab3fSAlgea Cao else 26938e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000; 26948e2bab3fSAlgea Cao 26958e2bab3fSAlgea Cao /* Check whether mode is out of phy cfg range. */ 26960594ce39SZhang Yubing ret = rockchip_phy_round_rate(conn->phy, rate); 26978e2bab3fSAlgea Cao 26988e2bab3fSAlgea Cao if (ret < 0) 26998e2bab3fSAlgea Cao edid_data->mode_buf[i].invalid = true; 27008e2bab3fSAlgea Cao } 27018e2bab3fSAlgea Cao } 2702