diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index 9cad6be8ee2609..00ff9b7d80e006 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -675,45 +675,15 @@ pinctrl-1 = <&I2C2_SCL_pmx_idle &I2C2_SDA_pmx_idle &I2C2_SCL_cfg_idle &I2C2_SDA_cfg_idle>; status = "ok"; - /* adv7533: adv7533@39 { - compatible = "adi,adv7511"; + compatible = "adi,adv7533"; reg = <0x39>; interrupt-parent = <&gpio1>; interrupts = <1 2>; - - gpio_pd = <&gpio0 4 0>; + pd-gpio = <&gpio0 4 0>; adi,input-depth = <8>; adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - }; - */ - - packet: adv7533@70 { - compatible = "adv7533,packet"; - reg = <0x38>; - }; - main: adv7533@7a { - compatible = "adv7533,main"; - reg = <0x39>; - has-regulator; - hdmi_vdd-supply = <&ldo15>; - hdmi_v1p2-supply = <&ldo22>; - gpio_int = <&gpio1 1 0>; - gpio_pd = <&gpio0 4 0>; - gpio_dsi_sel = <&gpio0 1 0>; }; - cec_dsi: adv7533@78 { - compatible = "adv7533,cec_dsi"; - reg = <0x3c>; - }; - edid: adv7533@7e { - compatible = "adv7533,edid"; - reg = <0x3f>; - }; - }; display-subsystem { @@ -752,10 +722,8 @@ reg = <0x0 0xf4107800 0x0 0x100>; clocks = <&clock_media HI6220_DSI_PCLK>; clock-names = "pclk_dsi"; - dsi_bit_clk_rate = <640>; vc = <0>; - //encoder-slave = <&adv7533>; - + encoder-slave = <&adv7533>; }; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 300405d5761821..18f16d74cc19d8 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -263,9 +263,9 @@ CONFIG_REGULATOR_HI6220=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_ANALOG_TV_SUPPORT=y # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -CONFIG_VIDEO_ADV7533=y CONFIG_DRM=y CONFIG_DRM_HISI=y +CONFIG_DRM_I2C_ADV7533=y CONFIG_FB_ARMCLCD=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3bf999134bcc50..1a77a49d269533 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1125,9 +1125,9 @@ EXPORT_SYMBOL(drm_edid_is_valid); * Return: 0 on success or -1 on failure. */ static int -drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, - int block, int len) +drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) { + struct i2c_adapter *adapter = data; unsigned char start = block * EDID_LENGTH; unsigned char segment = block >> 1; unsigned char xfers = segment ? 3 : 2; @@ -1184,8 +1184,26 @@ static bool drm_edid_is_zero(u8 *in_edid, int length) return true; } -static u8 * -drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) +/** + * drm_do_get_edid - get EDID data using a custom EDID block read function + * @connector: connector we're probing + * @get_edid_block: EDID block read function + * @data: private data passed to the block read function + * + * When the I2C adapter connected to the DDC bus is hidden behind a device that + * exposes a different interface to read EDID blocks this function can be used + * to get EDID data using a custom block read function. + * + * As in the general case the DDC bus is accessible by the kernel at the I2C + * level, drivers must make all reasonable efforts to expose it as an I2C + * adapter and use drm_get_edid() instead of abusing this function. + * + * Return: Pointer to valid EDID or NULL if we couldn't find any. + */ +struct edid *drm_do_get_edid(struct drm_connector *connector, + int (*get_edid_block)(void *data, u8 *buf, unsigned int block, + size_t len), + void *data) { int i, j = 0, valid_extensions = 0; u8 *block, *new; @@ -1196,7 +1214,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) /* base block fetch */ for (i = 0; i < 4; i++) { - if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) + if (get_edid_block(data, block, 0, EDID_LENGTH)) goto out; if (drm_edid_block_valid(block, 0, print_bad_edid)) break; @@ -1210,7 +1228,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) /* if there's no extensions, we're done */ if (block[0x7e] == 0) - return block; + return (struct edid *)block; new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); if (!new) @@ -1219,7 +1237,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) for (j = 1; j <= block[0x7e]; j++) { for (i = 0; i < 4; i++) { - if (drm_do_probe_ddc_edid(adapter, + if (get_edid_block(data, block + (valid_extensions + 1) * EDID_LENGTH, j, EDID_LENGTH)) goto out; @@ -1247,7 +1265,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) block = new; } - return block; + return (struct edid *)block; carp: if (print_bad_edid) { @@ -1260,6 +1278,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) kfree(block); return NULL; } +EXPORT_SYMBOL_GPL(drm_do_get_edid); /** * drm_probe_ddc() - probe DDC presence @@ -1289,12 +1308,10 @@ EXPORT_SYMBOL(drm_probe_ddc); struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { - struct edid *edid = NULL; - - if (drm_probe_ddc(adapter)) - edid = (struct edid *)drm_do_get_edid(connector, adapter); + if (!drm_probe_ddc(adapter)) + return NULL; - return edid; + return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); } EXPORT_SYMBOL(drm_get_edid); diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c index b8e9487a5edb84..7548ae2162ed40 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c @@ -101,6 +101,7 @@ struct hisi_drm_ade_crtc { u32 ade_core_rate; u32 media_noc_rate; u32 x , y; + bool first_time; struct drm_device *drm_dev; struct drm_crtc crtc; @@ -340,35 +341,50 @@ static int hisi_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, stride, (u32)obj->paddr, display_addr, fb->width, fb_hight); - /* TOP */ - writel(0, (ade_base + ADE_WDMA2_SRC_CFG_REG)); - writel(0, (ade_base + ADE_SCL3_MUX_CFG_REG)); - writel(0, (ade_base + ADE_SCL1_MUX_CFG_REG)); - writel(0, (ade_base + ADE_ROT_SRC_CFG_REG)); - writel(0, (ade_base + ADE_SCL2_SRC_CFG_REG)); - writel(0, (ade_base + ADE_SEC_OVLY_SRC_CFG_REG)); - writel(0, (ade_base + ADE_WDMA3_SRC_CFG_REG)); - writel(0, (ade_base + ADE_OVLY1_TRANS_CFG_REG)); - writel(0, (ade_base + ADE_CTRAN5_TRANS_CFG_REG)); - writel(0, (ade_base + ADE_OVLY_CTL_REG)); - - writel(0, (ade_base + ADE_SOFT_RST_SEL0_REG)); - writel(0, (ade_base + ADE_SOFT_RST_SEL1_REG)); - set_TOP_SOFT_RST_SEL0_disp_rdma(ade_base, 1); - set_TOP_SOFT_RST_SEL0_ctran5(ade_base, 1); - set_TOP_SOFT_RST_SEL0_ctran6(ade_base, 1); - - writel(0, (ade_base + ADE_RELOAD_DIS0_REG)); - writel(0, (ade_base + ADE_RELOAD_DIS1_REG)); - - writel(TOP_DISP_CH_SRC_RDMA, (ade_base + ADE_DISP_SRC_CFG_REG)); + if (crtc_ade->first_time) { + crtc_ade->first_time = false; + /* TOP */ + writel(0, (ade_base + ADE_WDMA2_SRC_CFG_REG)); + writel(0, (ade_base + ADE_SCL3_MUX_CFG_REG)); + writel(0, (ade_base + ADE_SCL1_MUX_CFG_REG)); + writel(0, (ade_base + ADE_ROT_SRC_CFG_REG)); + writel(0, (ade_base + ADE_SCL2_SRC_CFG_REG)); + writel(0, (ade_base + ADE_SEC_OVLY_SRC_CFG_REG)); + writel(0, (ade_base + ADE_WDMA3_SRC_CFG_REG)); + writel(0, (ade_base + ADE_OVLY1_TRANS_CFG_REG)); + writel(0, (ade_base + ADE_CTRAN5_TRANS_CFG_REG)); + writel(0, (ade_base + ADE_OVLY_CTL_REG)); + + writel(0, (ade_base + ADE_SOFT_RST_SEL0_REG)); + writel(0, (ade_base + ADE_SOFT_RST_SEL1_REG)); + set_TOP_SOFT_RST_SEL0_disp_rdma(ade_base, 1); + set_TOP_SOFT_RST_SEL0_ctran5(ade_base, 1); + set_TOP_SOFT_RST_SEL0_ctran6(ade_base, 1); + + writel(0, (ade_base + ADE_RELOAD_DIS0_REG)); + writel(0, (ade_base + ADE_RELOAD_DIS1_REG)); + + writel(TOP_DISP_CH_SRC_RDMA, (ade_base + ADE_DISP_SRC_CFG_REG)); + + /* ctran5 */ + writel(1, (ade_base + ADE_CTRAN5_DIS_REG)); + writel(fb->width * fb_hight - 1, + (ade_base + ADE_CTRAN5_IMAGE_SIZE_REG)); + writel(1, (ade_base + ADE_CTRAN5_CFG_OK_REG)); + + /* ctran6 */ + writel(1, (ade_base + ADE_CTRAN6_DIS_REG)); + writel(fb->width * fb_hight - 1, + (ade_base + ADE_CTRAN6_IMAGE_SIZE_REG)); + writel(1, (ade_base + ADE_CTRAN6_CFG_OK_REG)); + } /* DISP DMA */ if (16 == fb->bits_per_pixel) writel((ADE_RGB_565 << 16) & 0x1f0000, (ade_base + RD_CH_DISP_CTRL_REG)); else if (32 == fb->bits_per_pixel) - writel((ADE_ARGB_8888 << 16) & 0x1f0000, + writel((ADE_ABGR_8888 << 16) & 0x1f0000, (ade_base + RD_CH_DISP_CTRL_REG)); writel(display_addr, (ade_base + RD_CH_DISP_ADDR_REG)); @@ -377,18 +393,6 @@ static int hisi_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, writel(fb_hight * stride, (ade_base + RD_CH_DISP_SPACE_REG)); writel(1, (ade_base + RD_CH_DISP_EN_REG)); - /* ctran5 */ - writel(1, (ade_base + ADE_CTRAN5_DIS_REG)); - writel(fb->width * fb_hight - 1, - (ade_base + ADE_CTRAN5_IMAGE_SIZE_REG)); - writel(1, (ade_base + ADE_CTRAN5_CFG_OK_REG)); - - /* ctran6 */ - writel(1, (ade_base + ADE_CTRAN6_DIS_REG)); - writel(fb->width * fb_hight - 1, - (ade_base + ADE_CTRAN6_IMAGE_SIZE_REG)); - writel(1, (ade_base + ADE_CTRAN6_CFG_OK_REG)); - writel(1, (ade_base + ADE_EN_REG)); set_TOP_CTL_frm_end_start(ade_base, 1); set_LDI_CTRL_ldi_en(ade_base, ADE_ENABLE); @@ -442,6 +446,7 @@ static int hisi_drm_crtc_create(struct hisi_drm_ade_crtc *crtc_ade) int ret; crtc_ade->dpms = DRM_MODE_DPMS_OFF; + crtc_ade->first_time = true; ret = drm_crtc_init(crtc_ade->drm_dev, crtc, &crtc_funcs); if (ret < 0) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 1bf2b38f6864e0..b66905cca64997 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -46,6 +46,8 @@ #define DSI_BURST_MODE DSI_NON_BURST_SYNC_PULSES #define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define USE_DEFAULT_720P_MODE 1 + u8 *reg_base_mipi_dsi; @@ -571,7 +573,7 @@ int mipi_init(struct hisi_dsi *dsi) * Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period); */ - pixel_clk = dsi->vm.pixelclock / 1000; + pixel_clk = dsi->vm.pixelclock; hsa_time = dsi->vm.hsync_len * phy_register.lane_byte_clk / pixel_clk; hbp_time = dsi->vm.hback_porch * phy_register.lane_byte_clk / pixel_clk; hline_time = (dsi->vm.hsync_len + dsi->vm.hback_porch + @@ -581,6 +583,8 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_VID_HBP_TIME(hbp_time); set_MIPIDSI_VID_HLINE_TIME(hline_time); + DRM_INFO("%s,pixcel_clk=%d,dphy_freq=%d,hsa=%d,hbp=%d,hline=%d", __func__, + pixel_clk, dsi->dphy_freq, hsa_time, hbp_time, hline_time); /* * 5. Define the Vertical line configuration: * @@ -750,7 +754,7 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, struct drm_encoder_slave_funcs *sfuncs = get_slave_funcs(encoder); DRM_DEBUG_DRIVER("enter.\n"); - vm->pixelclock = mode->clock; + vm->pixelclock = mode->clock/1000; vm->hactive = mode->hdisplay; vm->vactive = mode->vdisplay; vm->vfront_porch = mode->vsync_start - mode->vdisplay; @@ -760,6 +764,9 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, vm->hback_porch = mode->htotal - mode->hsync_end; vm->hsync_len = mode->hsync_end - mode->hsync_start; + /* laneBitRate >= pixelClk*24/lanes */ + dsi->dphy_freq = vm->pixelclock*24/dsi->lanes + 20; + vm->flags = 0; if (mode->flags & DRM_MODE_FLAG_PHSYNC) vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; @@ -772,7 +779,8 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, if (sfuncs && sfuncs->mode_set) sfuncs->mode_set(encoder, mode, adjusted_mode); - DRM_DEBUG_DRIVER("exit success.\n"); + DRM_DEBUG_DRIVER("exit success: pixelclk=%d,dphy_freq=%d\n", + (u32)vm->pixelclock, dsi->dphy_freq); } static void hisi_drm_encoder_prepare(struct drm_encoder *encoder) @@ -816,7 +824,7 @@ hisi_dsi_detect(struct drm_connector *connector, bool force) status = sfuncs->detect(encoder, connector); DRM_DEBUG_DRIVER("exit success. status=%d\n", status); - return connector_status_connected; + return status; } static void hisi_dsi_connector_destroy(struct drm_connector *connector) @@ -832,6 +840,7 @@ static struct drm_connector_funcs hisi_dsi_connector_funcs = { .destroy = hisi_dsi_connector_destroy }; +#if USE_DEFAULT_720P_MODE static int hisi_get_default_modes(struct drm_connector *connector) { struct drm_display_mode *mode; @@ -860,6 +869,8 @@ static int hisi_get_default_modes(struct drm_connector *connector) DRM_DEBUG_DRIVER("exit successfully.\n"); return 1; } +#endif + static int hisi_dsi_get_modes(struct drm_connector *connector) { struct hisi_dsi *dsi = connector_to_dsi(connector); @@ -872,7 +883,11 @@ static int hisi_dsi_get_modes(struct drm_connector *connector) count = sfuncs->get_modes(encoder, connector); DRM_DEBUG_DRIVER("exit success. count=%d\n", count); +#if USE_DEFAULT_720P_MODE return hisi_get_default_modes(connector); +#else + return count; +#endif } static struct drm_encoder * @@ -894,6 +909,11 @@ static int hisi_drm_connector_mode_valid(struct drm_connector *connector, struct drm_encoder_slave_funcs *sfuncs = get_slave_funcs(encoder); int ret = MODE_OK; +#if USE_DEFAULT_720P_MODE + if (mode->vdisplay != 720) + return MODE_ONE_SIZE; +#endif + DRM_DEBUG_DRIVER("enter.\n"); if (sfuncs && sfuncs->mode_valid) ret = sfuncs->mode_valid(encoder, mode); @@ -912,6 +932,7 @@ static int hisi_drm_encoder_create(struct drm_device *dev, struct hisi_dsi *dsi) { /* int ret; */ struct drm_encoder *encoder = &dsi->base.base; + int ret; DRM_DEBUG_DRIVER("enter.\n"); dsi->dpms = DRM_MODE_DPMS_OFF; @@ -919,7 +940,6 @@ static int hisi_drm_encoder_create(struct drm_device *dev, struct hisi_dsi *dsi) drm_encoder_init(dev, encoder, &hisi_encoder_funcs, DRM_MODE_ENCODER_DSI); drm_encoder_helper_add(encoder, &hisi_encoder_helper_funcs); -#if 0 ret = dsi->drm_i2c_driver->encoder_init(dsi->client, dev, &dsi->base); if (ret) { DRM_ERROR("drm_i2c_encoder_init error\n"); @@ -930,7 +950,7 @@ static int hisi_drm_encoder_create(struct drm_device *dev, struct hisi_dsi *dsi) DRM_ERROR("failed check encoder function\n"); return -ENODEV; } -#endif + return 0; DRM_DEBUG_DRIVER("exit success.\n"); } @@ -957,10 +977,8 @@ static int hisi_dsi_probe(struct platform_device *pdev) struct hisi_dsi *dsi; struct resource *res; struct drm_device *dev = dev_get_platdata(&pdev->dev); -#if 0 struct device_node *slave_node; struct device_node *np = pdev->dev.of_node; -#endif DRM_DEBUG_DRIVER("enter.\n"); @@ -984,15 +1002,10 @@ static int hisi_dsi_probe(struct platform_device *pdev) ret = PTR_ERR(dsi->reg_base); } - ret = of_property_read_u32(pdev->dev.of_node, "dsi_bit_clk_rate", &dsi->dphy_freq); - if (ret) - dev_err(&pdev->dev, "failed to get dsi_bit_clk_rate"); - ret = of_property_read_u32(pdev->dev.of_node, "vc", &dsi->vc); if (ret) dev_err(&pdev->dev, "failed to get vc"); -#if 0 slave_node = of_parse_phandle(np, "encoder-slave", 0); if (!slave_node) { DRM_ERROR("failed to get slave encoder node\n"); @@ -1012,7 +1025,6 @@ static int hisi_dsi_probe(struct platform_device *pdev) DRM_ERROR("failed initialize encoder driver\n"); return -EPROBE_DEFER; } -#endif dsi->color_mode = DSI_24BITS_1; dsi->date_enable_pol = 0; diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hisi_drm_fbdev.c index fe49ae6d1bb11a..35e5ed5ab4a7f4 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_fbdev.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_fbdev.c @@ -238,7 +238,7 @@ static int hisi_drm_fbdev_probe(struct drm_fb_helper *helper, mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; mode_cmd.pixel_format = DRM_FORMAT_RGBA8888; - size = mode_cmd.pitches[0] * mode_cmd.height; + size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE); obj = drm_gem_cma_create(dev, size); if (IS_ERR(obj)) return -ENOMEM; diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 4d341db462a244..899796d21f4b3b 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -1,6 +1,12 @@ menu "I2C encoder or helper chips" depends on DRM && DRM_KMS_HELPER && I2C +config DRM_I2C_ADV7533 + tristate "ADV7533 encoder" + select REGMAP_I2C + help + Support for the Analog Device ADV7533 HDMI encoders. + config DRM_I2C_CH7006 tristate "Chrontel ch7006 TV encoder" default m if DRM_NOUVEAU diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 43aa33baebed8e..f6002059f8b9b2 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -1,5 +1,7 @@ ccflags-y := -Iinclude/drm +obj-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o + ch7006-y := ch7006_drv.o ch7006_mode.o obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o diff --git a/drivers/gpu/drm/i2c/adv7533.c b/drivers/gpu/drm/i2c/adv7533.c new file mode 100644 index 00000000000000..359bf70e8428d8 --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7533.c @@ -0,0 +1,757 @@ +/* + * Analog Devices ADV7533 HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7533.h" + +struct adv7533 { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_dsi; + + struct regmap *regmap_main; + struct regmap *regmap_dsi; + enum drm_connector_status status; + int dpms_mode; + + unsigned int f_tmds; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + + wait_queue_head_t wq; + struct drm_encoder *encoder; + + bool embedded_sync; + bool rgb; + + struct edid *edid; + + struct gpio_desc *gpio_pd; + +}; + +/* ADI recommended values for proper operation. */ +static const struct reg_default adv7533_fixed_registers[] = { + { 0x16, 0x20 }, + { 0x9a, 0xe0 }, + { 0xba, 0x70 }, + { 0xde, 0x82 }, + { 0xe4, 0x40 }, + { 0xe5, 0x80 }, +}; + +static const struct reg_default adv7533_dsi_fixed_registers[] = { + { 0x15, 0x10 }, + { 0x17, 0xd0 }, + { 0x24, 0x20 }, + { 0x57, 0x11 }, +}; + +static const struct regmap_config adv7533_main_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +#if 0 + .reg_defaults_raw = adv7533_register_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(adv7533_register_defaults), + + .volatile_reg = adv7533_register_volatile, +#endif +}; + +static const struct regmap_config adv7533_dsi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +}; + + +static struct adv7533 *encoder_to_adv7533(struct drm_encoder *encoder) +{ + return to_encoder_slave(encoder)->slave_priv; +} + +static const int edid_i2c_addr = 0x7e; +static const int packet_i2c_addr = 0x70; +static const int dsi_i2c_addr = 0x78; + +static const struct of_device_id adv7533_of_ids[] = { + { .compatible = "adi,adv7533"}, + { } +}; +MODULE_DEVICE_TABLE(of, adv7533_of_ids); + +/* ----------------------------------------------------------------------------- + * Hardware configuration + */ + +static void adv7533_set_colormap(struct adv7533 *adv7533, bool enable, + const uint16_t *coeff, + unsigned int scaling_factor) +{ + unsigned int i; + + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_CSC_UPPER(1), + ADV7533_CSC_UPDATE_MODE, ADV7533_CSC_UPDATE_MODE); + + if (enable) { + for (i = 0; i < 12; ++i) { + regmap_update_bits(adv7533->regmap_main, + ADV7533_REG_CSC_UPPER(i), + 0x1f, coeff[i] >> 8); + regmap_write(adv7533->regmap_main, + ADV7533_REG_CSC_LOWER(i), + coeff[i] & 0xff); + } + } + + if (enable) + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_CSC_UPPER(0), + 0xe0, 0x80 | (scaling_factor << 5)); + else + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_CSC_UPPER(0), + 0x80, 0x00); + + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_CSC_UPPER(1), + ADV7533_CSC_UPDATE_MODE, 0); +} + +static int adv7533_packet_enable(struct adv7533 *adv7533, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_PACKET_ENABLE0, + packet, 0xff); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_PACKET_ENABLE1, + packet, 0xff); + } + + return 0; +} + +static int adv7533_packet_disable(struct adv7533 *adv7533, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_PACKET_ENABLE0, + packet, 0x00); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_PACKET_ENABLE1, + packet, 0x00); + } + + return 0; +} + +/* Coefficients for adv7533 color space conversion */ +static const uint16_t adv7533_csc_ycbcr_to_rgb[] = { + 0x0734, 0x04ad, 0x0000, 0x1c1b, + 0x1ddc, 0x04ad, 0x1f24, 0x0135, + 0x0000, 0x04ad, 0x087c, 0x1b77, +}; + +static void adv7533_set_config_csc(struct adv7533 *adv7533, + struct drm_connector *connector, + bool rgb) +{ + struct adv7533_video_config config; + bool output_format_422, output_format_ycbcr; + unsigned int mode; + uint8_t infoframe[17]; + + if (adv7533->edid) + config.hdmi_mode = drm_detect_hdmi_monitor(adv7533->edid); + else + config.hdmi_mode = true; /* HDMI as default */ + + hdmi_avi_infoframe_init(&config.avi_infoframe); + + config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN; + + if (rgb) { + config.csc_enable = false; + config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + } else { + config.csc_scaling_factor = ADV7533_CSC_SCALING_4; + config.csc_coefficents = adv7533_csc_ycbcr_to_rgb; + + if ((connector->display_info.color_formats & + DRM_COLOR_FORMAT_YCRCB422) && + config.hdmi_mode) { + config.csc_enable = false; + config.avi_infoframe.colorspace = + HDMI_COLORSPACE_YUV422; + } else { + config.csc_enable = true; + config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + } + } + + if (config.hdmi_mode) { + mode = ADV7533_HDMI_CFG_MODE_HDMI; + + switch (config.avi_infoframe.colorspace) { + case HDMI_COLORSPACE_YUV444: + output_format_422 = false; + output_format_ycbcr = true; + break; + case HDMI_COLORSPACE_YUV422: + output_format_422 = true; + output_format_ycbcr = true; + break; + default: + output_format_422 = false; + output_format_ycbcr = false; + break; + } + } else { + mode = ADV7533_HDMI_CFG_MODE_DVI; + output_format_422 = false; + output_format_ycbcr = false; + } + + DRM_INFO("HDMI: mode=%d,format_422=%d,format_ycbcr=%d\n", mode, output_format_422, output_format_ycbcr); + adv7533_packet_disable(adv7533, ADV7533_PACKET_ENABLE_AVI_INFOFRAME); + + adv7533_set_colormap(adv7533, config.csc_enable, + config.csc_coefficents, + config.csc_scaling_factor); + + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_VIDEO_INPUT_CFG1, + 0x81, (output_format_422 << 7) | output_format_ycbcr); + + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_HDCP_HDMI_CFG, + ADV7533_HDMI_CFG_MODE_MASK, mode); + + hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe, + sizeof(infoframe)); + + /* The AVI infoframe id is not configurable */ + regmap_bulk_write(adv7533->regmap_main, ADV7533_REG_AVI_INFOFRAME_VERSION, + infoframe + 1, sizeof(infoframe) - 1); + + adv7533_packet_enable(adv7533, ADV7533_PACKET_ENABLE_AVI_INFOFRAME); +} + + +/* ----------------------------------------------------------------------------- + * Interrupt and hotplug detection + */ + +static bool adv7533_hpd(struct adv7533 *adv7533) +{ + unsigned int irq0; + int ret; + + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_INT(0), &irq0); + if (ret < 0) + return false; + + if (irq0 & ADV7533_INT0_HDP) { + regmap_write(adv7533->regmap_main, ADV7533_REG_INT(0), + ADV7533_INT0_HDP); + return true; + } + + return false; +} + +static irqreturn_t adv7533_irq_handler(int irq, void *devid) +{ + struct adv7533 *adv7533 = devid; + + if (adv7533->encoder && adv7533_hpd(adv7533)) + drm_helper_hpd_irq_event(adv7533->encoder->dev); + + wake_up_all(&adv7533->wq); + + return IRQ_HANDLED; +} + +static unsigned int adv7533_is_interrupt_pending(struct adv7533 *adv7533, + unsigned int irq) +{ + unsigned int irq0, irq1; + unsigned int pending; + int ret; + + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_INT(0), &irq0); + if (ret < 0) + return 0; + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_INT(1), &irq1); + if (ret < 0) + return 0; + + pending = (irq1 << 8) | irq0; + + return pending & irq; +} + +static int adv7533_wait_for_interrupt(struct adv7533 *adv7533, int irq, + int timeout) +{ + unsigned int pending; + int ret; + + if (adv7533->i2c_main->irq) { + ret = wait_event_interruptible_timeout(adv7533->wq, + adv7533_is_interrupt_pending(adv7533, irq), + msecs_to_jiffies(timeout)); + if (ret <= 0) + return 0; + pending = adv7533_is_interrupt_pending(adv7533, irq); + } else { + if (timeout < 25) + timeout = 25; + do { + pending = adv7533_is_interrupt_pending(adv7533, irq); + if (pending) + break; + msleep(25); + timeout -= 25; + } while (timeout >= 25); + } + + return pending; +} + +/* ----------------------------------------------------------------------------- + * EDID retrieval + */ + +static int adv7533_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + struct adv7533 *adv7533 = data; + struct i2c_msg xfer[2]; + uint8_t offset; + unsigned int i; + int ret; + + if (len > 128) + return -EINVAL; + + if (adv7533->current_edid_segment != block / 2) { + unsigned int status; + + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_DDC_STATUS, + &status); + if (ret < 0) + return ret; + + if (status != 2) { + regmap_write(adv7533->regmap_main, ADV7533_REG_EDID_SEGMENT, + block); + ret = adv7533_wait_for_interrupt(adv7533, + ADV7533_INT0_EDID_READY | + ADV7533_INT0_HDP, 200); + + if (!(ret & ADV7533_INT0_EDID_READY)) + return -EIO; + } + + regmap_write(adv7533->regmap_main, ADV7533_REG_INT(0), + ADV7533_INT0_EDID_READY | ADV7533_INT0_HDP); + + /* Break this apart, hopefully more I2C controllers will + * support 64 byte transfers than 256 byte transfers + */ + + xfer[0].addr = adv7533->i2c_edid->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = &offset; + xfer[1].addr = adv7533->i2c_edid->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 64; + xfer[1].buf = adv7533->edid_buf; + + offset = 0; + + for (i = 0; i < 4; ++i) { + ret = i2c_transfer(adv7533->i2c_edid->adapter, xfer, + ARRAY_SIZE(xfer)); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + xfer[1].buf += 64; + offset += 64; + } + + adv7533->current_edid_segment = block / 2; + } + + if (block % 2 == 0) + memcpy(buf, adv7533->edid_buf, len); + else + memcpy(buf, adv7533->edid_buf + 128, len); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Encoder operations + */ + +static int adv7533_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7533 *adv7533 = encoder_to_adv7533(encoder); + struct edid *edid; + unsigned int count; + + /* Reading the EDID only works if the device is powered */ + if (adv7533->dpms_mode != DRM_MODE_DPMS_ON) { + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER2, + BIT(6), BIT(6)); + regmap_write(adv7533->regmap_main, ADV7533_REG_INT(0), + ADV7533_INT0_EDID_READY | ADV7533_INT0_HDP); + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER, + ADV7533_POWER_POWER_DOWN, 0); + adv7533->current_edid_segment = -1; + } + + edid = drm_do_get_edid(connector, adv7533_get_edid_block, adv7533); + /* edid = drm_get_edid(connector, adv7533->i2c_edid->adapter); */ + + if (adv7533->dpms_mode != DRM_MODE_DPMS_ON) + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER, + ADV7533_POWER_POWER_DOWN, + ADV7533_POWER_POWER_DOWN); + + adv7533->edid = edid; + adv7533_set_config_csc(adv7533, connector, true); /* adv7533->rgb); */ + if (!edid) { + DRM_INFO("May has one empty edid block!\n"); + return 0; + } + + drm_mode_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + kfree(adv7533->edid); + adv7533->edid = NULL; + return count; +} + +static void adv7533_dsi_receiver_dpms(struct adv7533 *adv7533, int mode) +{ + switch (mode) { + case DRM_MODE_DPMS_ON: + regmap_write(adv7533->regmap_dsi, 0x03, 0x89); + regmap_write(adv7533->regmap_dsi, 0x27, 0x0b); /* Timing generator off */ + regmap_write(adv7533->regmap_dsi, 0x1C, 0x30); /* 3 lanes */ + break; + default: + regmap_write(adv7533->regmap_dsi, 0x03, 0x0b); + break; + } +} + +static void adv7533_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct adv7533 *adv7533 = encoder_to_adv7533(encoder); + + switch (mode) { + case DRM_MODE_DPMS_ON: + adv7533->current_edid_segment = -1; + + regmap_write(adv7533->regmap_main, ADV7533_REG_INT(0), + ADV7533_INT0_EDID_READY | ADV7533_INT0_HDP); + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER, + ADV7533_POWER_POWER_DOWN, 0); + /* + * Per spec it is allowed to pulse the HDP signal to indicate + * that the EDID information has changed. Some monitors do this + * when they wakeup from standby or are enabled. When the HDP + * goes low the adv7533 is reset and the outputs are disabled + * which might cause the monitor to go to standby again. To + * avoid this we ignore the HDP pin for the first few seconds + * after enabeling the output. + */ + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER2, + BIT(6), BIT(6)); + /* Most of the registers are reset during power down or + * when HPD is low + */ + regcache_sync(adv7533->regmap_main); + break; + default: + /* TODO: setup additional power down modes */ + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER, + ADV7533_POWER_POWER_DOWN, + ADV7533_POWER_POWER_DOWN); + regcache_mark_dirty(adv7533->regmap_main); + break; + } + + /* dsi receiver dpms */ + adv7533_dsi_receiver_dpms(adv7533, mode); + adv7533->dpms_mode = mode; +} + +static enum drm_connector_status +adv7533_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7533 *adv7533 = encoder_to_adv7533(encoder); + enum drm_connector_status status; + unsigned int val; + bool hpd; + int ret; + + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_STATUS, &val); + if (ret < 0) + return connector_status_disconnected; + + if (val & ADV7533_STATUS_HPD) + status = connector_status_connected; + else + status = connector_status_disconnected; + + hpd = adv7533_hpd(adv7533); + + /* The chip resets itself when the cable is disconnected, so in case + * there is a pending HPD interrupt and the cable is connected there was + * at least one transition from disconnected to connected and the chip + * has to be reinitialized. */ + if (status == connector_status_connected && hpd && + adv7533->dpms_mode == DRM_MODE_DPMS_ON) { + regcache_mark_dirty(adv7533->regmap_main); + adv7533_encoder_dpms(encoder, adv7533->dpms_mode); + adv7533_get_modes(encoder, connector); + if (adv7533->status == connector_status_connected) + status = connector_status_disconnected; + } else { + /* Renable HDP sensing */ + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER2, + BIT(6), 0); + } + + DRM_INFO("HDMI: new_status=%d,old_status=%d,hpd=%d,dpms=%d\n", + status, adv7533->status, hpd, adv7533->dpms_mode); + adv7533->status = status; + return status; +} + +static int adv7533_encoder_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + return MODE_OK; +} + +static void adv7533_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ +#if 0 + struct adv7533 *adv7533 = encoder_to_adv7533(encoder); + unsigned int low_refresh_rate; +#endif +} + +static struct drm_encoder_slave_funcs adv7533_encoder_funcs = { + .dpms = adv7533_encoder_dpms, + .mode_valid = adv7533_encoder_mode_valid, + .mode_set = adv7533_encoder_mode_set, + .detect = adv7533_encoder_detect, + .get_modes = adv7533_get_modes, +}; + +static int adv7533_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct adv7533 *adv7533; + struct device *dev = &i2c->dev; + unsigned int val; + int ret; + + DRM_DEBUG_DRIVER("enter.\n"); + if (!dev->of_node) + return -EINVAL; + + adv7533 = devm_kzalloc(dev, sizeof(*adv7533), GFP_KERNEL); + if (!adv7533) + return -ENOMEM; + + adv7533->dpms_mode = DRM_MODE_DPMS_OFF; + adv7533->status = connector_status_disconnected; + + /* + * The power down GPIO is optional. If present, toggle it from active to + * inactive to wake up the encoder. + */ + adv7533->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); + if (IS_ERR(adv7533->gpio_pd)) + return PTR_ERR(adv7533->gpio_pd); + + if (adv7533->gpio_pd) { + mdelay(5); + gpiod_set_value_cansleep(adv7533->gpio_pd, 0); + } + + adv7533->regmap_main = devm_regmap_init_i2c(i2c, &adv7533_main_regmap_config); + if (IS_ERR(adv7533->regmap_main)) + return PTR_ERR(adv7533->regmap_main); + + ret = regmap_read(adv7533->regmap_main, ADV7533_REG_CHIP_REVISION, &val); + if (ret) + return ret; + dev_dbg(dev, "Rev. %d\n", val); + ret = regmap_register_patch(adv7533->regmap_main, adv7533_fixed_registers, + ARRAY_SIZE(adv7533_fixed_registers)); + if (ret) + return ret; + + regmap_write(adv7533->regmap_main, ADV7533_REG_EDID_I2C_ADDR, edid_i2c_addr); + regmap_write(adv7533->regmap_main, ADV7533_REG_PACKET_I2C_ADDR, + packet_i2c_addr); + regmap_write(adv7533->regmap_main, ADV7533_REG_CEC_I2C_ADDR, dsi_i2c_addr); + adv7533_packet_disable(adv7533, 0xffff); + + adv7533->i2c_main = i2c; + adv7533->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); + if (!adv7533->i2c_edid) + return -ENOMEM; + + adv7533->i2c_dsi = i2c_new_dummy(i2c->adapter, dsi_i2c_addr >> 1); + if (!adv7533->i2c_dsi) { + ret = -ENOMEM; + goto err_i2c_unregister_edid; + } + + adv7533->regmap_dsi = devm_regmap_init_i2c(adv7533->i2c_dsi, + &adv7533_dsi_regmap_config); + if (IS_ERR(adv7533->regmap_dsi)) { + ret = PTR_ERR(adv7533->regmap_dsi); + goto err_i2c_unregister_dsi; + } + + ret = regmap_register_patch(adv7533->regmap_dsi, adv7533_dsi_fixed_registers, + ARRAY_SIZE(adv7533_dsi_fixed_registers)); + if (ret) + return ret; + + if (i2c->irq) { + init_waitqueue_head(&adv7533->wq); + + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, + adv7533_irq_handler, + IRQF_ONESHOT, dev_name(dev), + adv7533); + if (ret) + goto err_i2c_unregister_dsi; + } + + /* CEC is unused for now */ + regmap_write(adv7533->regmap_main, ADV7533_REG_CEC_CTRL, + ADV7533_CEC_CTRL_POWER_DOWN); + + regmap_update_bits(adv7533->regmap_main, ADV7533_REG_POWER, + ADV7533_POWER_POWER_DOWN, ADV7533_POWER_POWER_DOWN); + + adv7533->current_edid_segment = -1; + + i2c_set_clientdata(i2c, adv7533); + + DRM_DEBUG_DRIVER("exit success.\n"); + return 0; + +err_i2c_unregister_dsi: + i2c_unregister_device(adv7533->i2c_dsi); +err_i2c_unregister_edid: + i2c_unregister_device(adv7533->i2c_edid); + + return ret; +} + +static int adv7533_remove(struct i2c_client *i2c) +{ + struct adv7533 *adv7533 = i2c_get_clientdata(i2c); + + i2c_unregister_device(adv7533->i2c_dsi); + i2c_unregister_device(adv7533->i2c_edid); + + return 0; +} + +static int adv7533_encoder_init(struct i2c_client *i2c, struct drm_device *dev, + struct drm_encoder_slave *encoder) +{ + + struct adv7533 *adv7533 = i2c_get_clientdata(i2c); + + encoder->slave_priv = adv7533; + encoder->slave_funcs = &adv7533_encoder_funcs; + + adv7533->encoder = &encoder->base; + + return 0; +} + +static const struct i2c_device_id adv7533_i2c_ids[] = { + { "adv7533", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7533_i2c_ids); + +static struct drm_i2c_encoder_driver adv7533_driver = { + .i2c_driver = { + .driver = { + .name = "adv7533", + .of_match_table = adv7533_of_ids, + }, + .id_table = adv7533_i2c_ids, + .probe = adv7533_probe, + .remove = adv7533_remove, + }, + + .encoder_init = adv7533_encoder_init, +}; + +static int __init adv7533_init(void) +{ + return drm_i2c_encoder_register(THIS_MODULE, &adv7533_driver); +} +module_init(adv7533_init); + +static void __exit adv7533_exit(void) +{ + drm_i2c_encoder_unregister(&adv7533_driver); +} +module_exit(adv7533_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ADV7533 HDMI transmitter driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/i2c/adv7533.h b/drivers/gpu/drm/i2c/adv7533.h new file mode 100644 index 00000000000000..90d3e098571aed --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7533.h @@ -0,0 +1,223 @@ +/* + * Analog Devices ADV7533 HDMI transmitter driver + * + * Copyright 2015 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __DRM_I2C_ADV7533_H__ +#define __DRM_I2C_ADV7533_H__ + +#include + +#define ADV7533_REG_CHIP_REVISION 0x00 +#define ADV7533_REG_N0 0x01 +#define ADV7533_REG_N1 0x02 +#define ADV7533_REG_N2 0x03 +#define ADV7533_REG_SPDIF_FREQ 0x04 +#define ADV7533_REG_CTS_AUTOMATIC1 0x05 +#define ADV7533_REG_CTS_AUTOMATIC2 0x06 +#define ADV7533_REG_CTS_MANUAL0 0x07 +#define ADV7533_REG_CTS_MANUAL1 0x08 +#define ADV7533_REG_CTS_MANUAL2 0x09 +#define ADV7533_REG_AUDIO_SOURCE 0x0a +#define ADV7533_REG_AUDIO_CONFIG 0x0b +#define ADV7533_REG_I2S_CONFIG 0x0c +#define ADV7533_REG_I2S_WIDTH 0x0d +#define ADV7533_REG_AUDIO_SUB_SRC0 0x0e +#define ADV7533_REG_AUDIO_SUB_SRC1 0x0f +#define ADV7533_REG_AUDIO_SUB_SRC2 0x10 +#define ADV7533_REG_AUDIO_SUB_SRC3 0x11 +#define ADV7533_REG_AUDIO_CFG1 0x12 +#define ADV7533_REG_AUDIO_CFG2 0x13 +#define ADV7533_REG_AUDIO_CFG3 0x14 +#define ADV7533_REG_I2C_FREQ_ID_CFG 0x15 +#define ADV7533_REG_VIDEO_INPUT_CFG1 0x16 +#define ADV7533_REG_CSC_UPPER(x) (0x18 + (x) * 2) +#define ADV7533_REG_CSC_LOWER(x) (0x19 + (x) * 2) +#define ADV7533_REG_SYNC_DECODER(x) (0x30 + (x)) +#define ADV7533_REG_DE_GENERATOR (0x35 + (x)) +#define ADV7533_REG_PIXEL_REPETITION 0x3b +#define ADV7533_REG_VIC_MANUAL 0x3c +#define ADV7533_REG_VIC_SEND 0x3d +#define ADV7533_REG_VIC_DETECTED 0x3e +#define ADV7533_REG_AUX_VIC_DETECTED 0x3f +#define ADV7533_REG_PACKET_ENABLE0 0x40 +#define ADV7533_REG_POWER 0x41 +#define ADV7533_REG_STATUS 0x42 +#define ADV7533_REG_EDID_I2C_ADDR 0x43 +#define ADV7533_REG_PACKET_ENABLE1 0x44 +#define ADV7533_REG_PACKET_I2C_ADDR 0x45 +#define ADV7533_REG_DSD_ENABLE 0x46 +#define ADV7533_REG_VIDEO_INPUT_CFG2 0x48 +#define ADV7533_REG_INFOFRAME_UPDATE 0x4a +#define ADV7533_REG_GC(x) (0x4b + (x)) /* 0x4b - 0x51 */ +#define ADV7533_REG_AVI_INFOFRAME_VERSION 0x52 +#define ADV7533_REG_AVI_INFOFRAME_LENGTH 0x53 +#define ADV7533_REG_AVI_INFOFRAME_CHECKSUM 0x54 +#define ADV7533_REG_AVI_INFOFRAME(x) (0x55 + (x)) /* 0x55 - 0x6f */ +#define ADV7533_REG_AUDIO_INFOFRAME_VERSION 0x70 +#define ADV7533_REG_AUDIO_INFOFRAME_LENGTH 0x71 +#define ADV7533_REG_AUDIO_INFOFRAME_CHECKSUM 0x72 +#define ADV7533_REG_AUDIO_INFOFRAME(x) (0x73 + (x)) /* 0x73 - 0x7c */ +#define ADV7533_REG_INT_ENABLE(x) (0x94 + (x)) +#define ADV7533_REG_INT(x) (0x96 + (x)) +#define ADV7533_REG_INPUT_CLK_DIV 0x9d +#define ADV7533_REG_PLL_STATUS 0x9e +#define ADV7533_REG_HDMI_POWER 0xa1 +#define ADV7533_REG_HDCP_HDMI_CFG 0xaf +#define ADV7533_REG_AN(x) (0xb0 + (x)) /* 0xb0 - 0xb7 */ +#define ADV7533_REG_HDCP_STATUS 0xb8 +#define ADV7533_REG_BCAPS 0xbe +#define ADV7533_REG_BKSV(x) (0xc0 + (x)) /* 0xc0 - 0xc3 */ +#define ADV7533_REG_EDID_SEGMENT 0xc4 +#define ADV7533_REG_DDC_STATUS 0xc8 +#define ADV7533_REG_EDID_READ_CTRL 0xc9 +#define ADV7533_REG_BSTATUS(x) (0xca + (x)) /* 0xca - 0xcb */ +#define ADV7533_REG_TIMING_GEN_SEQ 0xd0 +#define ADV7533_REG_POWER2 0xd6 +#define ADV7533_REG_HSYNC_PLACEMENT_MSB 0xfa + +#define ADV7533_REG_SYNC_ADJUSTMENT(x) (0xd7 + (x)) /* 0xd7 - 0xdc */ +#define ADV7533_REG_TMDS_CLOCK_INV 0xde +#define ADV7533_REG_ARC_CTRL 0xdf +#define ADV7533_REG_CEC_I2C_ADDR 0xe1 +#define ADV7533_REG_CEC_CTRL 0xe2 +#define ADV7533_REG_CHIP_ID_HIGH 0xf5 +#define ADV7533_REG_CHIP_ID_LOW 0xf6 + +#define ADV7533_CSC_ENABLE BIT(7) +#define ADV7533_CSC_UPDATE_MODE BIT(5) + +#define ADV7533_INT0_HDP BIT(7) +#define ADV7533_INT0_VSYNC BIT(5) +#define ADV7533_INT0_AUDIO_FIFO_FULL BIT(4) +#define ADV7533_INT0_EDID_READY BIT(2) +#define ADV7533_INT0_HDCP_AUTHENTICATED BIT(1) + +#define ADV7533_INT1_DDC_ERROR BIT(7) +#define ADV7533_INT1_BKSV BIT(6) +#define ADV7533_INT1_CEC_TX_READY BIT(5) +#define ADV7533_INT1_CEC_TX_ARBIT_LOST BIT(4) +#define ADV7533_INT1_CEC_TX_RETRY_TIMEOUT BIT(3) +#define ADV7533_INT1_CEC_RX_READY3 BIT(2) +#define ADV7533_INT1_CEC_RX_READY2 BIT(1) +#define ADV7533_INT1_CEC_RX_READY1 BIT(0) + +#define ADV7533_ARC_CTRL_POWER_DOWN BIT(0) + +#define ADV7533_CEC_CTRL_POWER_DOWN BIT(0) + +#define ADV7533_POWER_POWER_DOWN BIT(6) + +#define ADV7533_HDMI_CFG_MODE_MASK 0x2 +#define ADV7533_HDMI_CFG_MODE_DVI 0x0 +#define ADV7533_HDMI_CFG_MODE_HDMI 0x2 + +#define ADV7533_AUDIO_SELECT_I2C 0x0 +#define ADV7533_AUDIO_SELECT_SPDIF 0x1 +#define ADV7533_AUDIO_SELECT_DSD 0x2 +#define ADV7533_AUDIO_SELECT_HBR 0x3 +#define ADV7533_AUDIO_SELECT_DST 0x4 + +#define ADV7533_I2S_SAMPLE_LEN_16 0x2 +#define ADV7533_I2S_SAMPLE_LEN_20 0x3 +#define ADV7533_I2S_SAMPLE_LEN_18 0x4 +#define ADV7533_I2S_SAMPLE_LEN_22 0x5 +#define ADV7533_I2S_SAMPLE_LEN_19 0x8 +#define ADV7533_I2S_SAMPLE_LEN_23 0x9 +#define ADV7533_I2S_SAMPLE_LEN_24 0xb +#define ADV7533_I2S_SAMPLE_LEN_17 0xc +#define ADV7533_I2S_SAMPLE_LEN_21 0xd + +#define ADV7533_SAMPLE_FREQ_44100 0x0 +#define ADV7533_SAMPLE_FREQ_48000 0x2 +#define ADV7533_SAMPLE_FREQ_32000 0x3 +#define ADV7533_SAMPLE_FREQ_88200 0x8 +#define ADV7533_SAMPLE_FREQ_96000 0xa +#define ADV7533_SAMPLE_FREQ_176400 0xc +#define ADV7533_SAMPLE_FREQ_192000 0xe + +#define ADV7533_STATUS_POWER_DOWN_POLARITY BIT(7) +#define ADV7533_STATUS_HPD BIT(6) +#define ADV7533_STATUS_MONITOR_SENSE BIT(5) +#define ADV7533_STATUS_I2S_32BIT_MODE BIT(3) + +#define ADV7533_PACKET_ENABLE_N_CTS BIT(8+6) +#define ADV7533_PACKET_ENABLE_AUDIO_SAMPLE BIT(8+5) +#define ADV7533_PACKET_ENABLE_AVI_INFOFRAME BIT(8+4) +#define ADV7533_PACKET_ENABLE_AUDIO_INFOFRAME BIT(8+3) +#define ADV7533_PACKET_ENABLE_GC BIT(7) +#define ADV7533_PACKET_ENABLE_SPD BIT(6) +#define ADV7533_PACKET_ENABLE_MPEG BIT(5) +#define ADV7533_PACKET_ENABLE_ACP BIT(4) +#define ADV7533_PACKET_ENABLE_ISRC BIT(3) +#define ADV7533_PACKET_ENABLE_GM BIT(2) +#define ADV7533_PACKET_ENABLE_SPARE2 BIT(1) +#define ADV7533_PACKET_ENABLE_SPARE1 BIT(0) + +#define ADV7533_REG_POWER2_HDP_SRC_MASK 0xc0 +#define ADV7533_REG_POWER2_HDP_SRC_BOTH 0x00 +#define ADV7533_REG_POWER2_HDP_SRC_HDP 0x40 +#define ADV7533_REG_POWER2_HDP_SRC_CEC 0x80 +#define ADV7533_REG_POWER2_HDP_SRC_NONE 0xc0 +#define ADV7533_REG_POWER2_TDMS_ENABLE BIT(4) +#define ADV7533_REG_POWER2_GATE_INPUT_CLK BIT(0) + +#define ADV7533_LOW_REFRESH_RATE_NONE 0x0 +#define ADV7533_LOW_REFRESH_RATE_24HZ 0x1 +#define ADV7533_LOW_REFRESH_RATE_25HZ 0x2 +#define ADV7533_LOW_REFRESH_RATE_30HZ 0x3 + +#define ADV7533_AUDIO_CFG3_LEN_MASK 0x0f +#define ADV7533_I2C_FREQ_ID_CFG_RATE_MASK 0xf0 + +#define ADV7533_AUDIO_SOURCE_I2S 0 +#define ADV7533_AUDIO_SOURCE_SPDIF 1 + +#define ADV7533_I2S_FORMAT_I2S 0 +#define ADV7533_I2S_FORMAT_RIGHT_J 1 +#define ADV7533_I2S_FORMAT_LEFT_J 2 + +#define ADV7533_PACKET(p, x) ((p) * 0x20 + (x)) +#define ADV7533_PACKET_SDP(x) ADV7533_PACKET(0, x) +#define ADV7533_PACKET_MPEG(x) ADV7533_PACKET(1, x) +#define ADV7533_PACKET_ACP(x) ADV7533_PACKET(2, x) +#define ADV7533_PACKET_ISRC1(x) ADV7533_PACKET(3, x) +#define ADV7533_PACKET_ISRC2(x) ADV7533_PACKET(4, x) +#define ADV7533_PACKET_GM(x) ADV7533_PACKET(5, x) +#define ADV7533_PACKET_SPARE(x) ADV7533_PACKET(6, x) + + +/** + * enum adv7533_csc_scaling - Scaling factor for the ADV7533 CSC + * @ADV7533_CSC_SCALING_1: CSC results are not scaled + * @ADV7533_CSC_SCALING_2: CSC results are scaled by a factor of two + * @ADV7533_CSC_SCALING_4: CSC results are scalled by a factor of four + */ +enum adv7533_csc_scaling { + ADV7533_CSC_SCALING_1 = 0, + ADV7533_CSC_SCALING_2 = 1, + ADV7533_CSC_SCALING_4 = 2, +}; + +/** + * struct adv7533_video_config - Describes adv7533 hardware configuration + * @csc_enable: Whether to enable color space conversion + * @csc_scaling_factor: Color space conversion scaling factor + * @csc_coefficents: Color space conversion coefficents + * @hdmi_mode: Whether to use HDMI or DVI output mode + * @avi_infoframe: HDMI infoframe + */ +struct adv7533_video_config { + bool csc_enable; + enum adv7533_csc_scaling csc_scaling_factor; + const uint16_t *csc_coefficents; + + bool hdmi_mode; + struct hdmi_avi_infoframe avi_infoframe; +}; + + +#endif /* __DRM_I2C_ADV7533_H__ */ diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index b96031d947a0c5..c30fa4e48f1467 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -279,4 +279,9 @@ int drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, const struct drm_display_mode *mode); +struct edid *drm_do_get_edid(struct drm_connector *connector, + int (*get_edid_block)(void *data, u8 *buf, unsigned int block, + size_t len), + void *data); + #endif /* __DRM_EDID_H__ */