From 026108bcf4b58c7c7fbddd07ef449100c9fd9dfc Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 3 Jun 2015 10:38:27 +0800 Subject: [PATCH 01/28] hikey quench initcall logspam We only need to fill the logs with this if something is dying. Signed-off-by: Andy Green --- init/main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/init/main.c b/init/main.c index 321d0ceb26d378..464de848ddc50d 100644 --- a/init/main.c +++ b/init/main.c @@ -9,8 +9,6 @@ * Simplified starting of init: Michael A. Griffith */ -#define DEBUG /* Enable initcall_debug */ - #include #include #include @@ -764,13 +762,13 @@ static int __init_or_module do_one_initcall_debug(initcall_t fn) unsigned long long duration; int ret; - printk(KERN_DEBUG "calling %pF @ %i\n", fn, task_pid_nr(current)); + pr_debug("calling %pF @ %i\n", fn, task_pid_nr(current)); calltime = ktime_get(); ret = fn(); rettime = ktime_get(); delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n", + pr_debug("initcall %pF returned %d after %lld usecs\n", fn, ret, duration); return ret; From 1768cb100653f0f2471b76928883d1b5beb7420b Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 3 Jun 2015 19:25:16 +0800 Subject: [PATCH 02/28] config update cma to 64MB Otherwise we can't even make a 1080p framebuffer Signed-off-by: Andy Green --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2f0475f73e4fc6..9f46da15e0b64a 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -190,6 +190,7 @@ CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=64 CONFIG_CONNECTOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y From c3c0c9ae0189bb94d06e066214d51a1a3c145ee3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 11 Jun 2015 09:11:16 +0800 Subject: [PATCH 03/28] drm adv7533 remove driver from tree Signed-off-by: Andy Green --- drivers/gpu/drm/i2c/Kconfig | 6 - drivers/gpu/drm/i2c/Makefile | 2 - drivers/gpu/drm/i2c/adv7533.c | 734 ---------------------------------- drivers/gpu/drm/i2c/adv7533.h | 223 ----------- 4 files changed, 965 deletions(-) delete mode 100644 drivers/gpu/drm/i2c/adv7533.c delete mode 100644 drivers/gpu/drm/i2c/adv7533.h diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 899796d21f4b3b..4d341db462a244 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -1,12 +1,6 @@ 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 f6002059f8b9b2..43aa33baebed8e 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -1,7 +1,5 @@ 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 deleted file mode 100644 index 47888ee38943c1..00000000000000 --- a/drivers/gpu/drm/i2c/adv7533.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * 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; - int dpms_mode; - - unsigned int current_edid_segment; - uint8_t edid_buf[256]; - - wait_queue_head_t wq; - struct delayed_work hotplug_work; - struct drm_encoder *encoder; - - 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, -}; - -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 = false; - - 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) - return true; - - return false; -} - -static void adv7533_hotplug_work_func(struct work_struct *work) -{ - struct adv7533 *adv7533; - bool hpd_event_sent; - - DRM_INFO("HDMI: hpd work in\n"); - adv7533 = container_of(work, struct adv7533, hotplug_work.work); - - if (adv7533->encoder && adv7533_hpd(adv7533)) { - hpd_event_sent = drm_helper_hpd_irq_event(adv7533->encoder->dev); - DRM_INFO("HDMI: hpd_event_sent=%d\n", hpd_event_sent); - } - - wake_up_all(&adv7533->wq); - DRM_INFO("HDMI: hpd work out\n"); -} - -static irqreturn_t adv7533_irq_handler(int irq, void *devid) -{ - struct adv7533 *adv7533 = devid; - - mod_delayed_work(system_wq, &adv7533->hotplug_work, - msecs_to_jiffies(1500)); - - 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; - - DRM_INFO("HDMI: status=0x%x, ret=%d\n", status, ret); - if (status != 2) { - /* Open EDID_READY and DDC_ERROR interrupts */ - regmap_update_bits(adv7533->regmap_main, ADV7533_REG_INT_ENABLE(0), - ADV7533_INT0_EDID_READY, ADV7533_INT0_EDID_READY); - regmap_update_bits(adv7533->regmap_main, ADV7533_REG_INT_ENABLE(1), - ADV7533_INT1_DDC_ERROR, ADV7533_INT1_DDC_ERROR); - regmap_write(adv7533->regmap_main, ADV7533_REG_EDID_SEGMENT, - block); - ret = adv7533_wait_for_interrupt(adv7533, - ADV7533_INT0_EDID_READY | - (ADV7533_INT1_DDC_ERROR << 8), 200); - - DRM_INFO("HDMI: ret=0x%x\n", ret); - if (!(ret & ADV7533_INT0_EDID_READY)) - return -EIO; - } - - - /* 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_POWER, - ADV7533_POWER_POWER_DOWN, 0); - adv7533->current_edid_segment = -1; - msleep(200); /* wait for EDID finish reading */ - } - - edid = drm_do_get_edid(connector, adv7533_get_edid_block, adv7533); - - 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); - if (!edid) { - DRM_ERROR("Reading edid block error!\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_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); - - DRM_INFO("HDMI: status=%d,hpd=%d,dpms=%d\n", - status, hpd, adv7533->dpms_mode); - 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) -{ -} - -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; - - /* - * 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); - - INIT_DELAYED_WORK(&adv7533->hotplug_work, adv7533_hotplug_work_func); - 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 deleted file mode 100644 index 90d3e098571aed..00000000000000 --- a/drivers/gpu/drm/i2c/adv7533.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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__ */ From 3b8e66a0fb3c912f7b8ad316113cfec89fba8712 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 11 Jun 2015 06:25:02 +0800 Subject: [PATCH 04/28] drm import v4.1 rc7 adv7511.c Signed-off-by: Andy Green --- arch/arm64/configs/defconfig | 2 +- drivers/gpu/drm/i2c/Kconfig | 6 + drivers/gpu/drm/i2c/Makefile | 2 + drivers/gpu/drm/i2c/adv7511.c | 1014 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/i2c/adv7511.h | 289 ++++++++++ 5 files changed, 1312 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i2c/adv7511.c create mode 100644 drivers/gpu/drm/i2c/adv7511.h diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 9f46da15e0b64a..77b4876630ba3f 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -277,7 +277,7 @@ CONFIG_MALI_SHARED_INTERRUPTS=y CONFIG_MALI_DT=y CONFIG_MALI_PLAT_SPECIFIC_DT=y CONFIG_DRM=y -CONFIG_DRM_I2C_ADV7533=y +CONFIG_DRM_I2C_ADV7511=y CONFIG_DRM_HISI=y CONFIG_ION=y CONFIG_ION_HISI=y diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 4d341db462a244..f96263052897ea 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_ADV7511 + tristate "AV7511 encoder" + select REGMAP_I2C + help + Support for the Analog Device ADV7511(W) and ADV7513 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..2c72eb584ab7c6 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_ADV7511) += adv7511.o + ch7006-y := ch7006_drv.o ch7006_mode.o obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c new file mode 100644 index 00000000000000..b728523e194f75 --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -0,0 +1,1014 @@ +/* + * Analog Devices ADV7511 HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7511.h" + +struct adv7511 { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + + struct regmap *regmap; + struct regmap *packet_memory_regmap; + enum drm_connector_status status; + bool powered; + + unsigned int f_tmds; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + + wait_queue_head_t wq; + struct drm_encoder *encoder; + + bool embedded_sync; + enum adv7511_sync_polarity vsync_polarity; + enum adv7511_sync_polarity hsync_polarity; + bool rgb; + + struct edid *edid; + + struct gpio_desc *gpio_pd; +}; + +static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) +{ + return to_encoder_slave(encoder)->slave_priv; +} + +/* ADI recommended values for proper operation. */ +static const struct reg_default adv7511_fixed_registers[] = { + { 0x98, 0x03 }, + { 0x9a, 0xe0 }, + { 0x9c, 0x30 }, + { 0x9d, 0x61 }, + { 0xa2, 0xa4 }, + { 0xa3, 0xa4 }, + { 0xe0, 0xd0 }, + { 0xf9, 0x00 }, + { 0x55, 0x02 }, +}; + +/* ----------------------------------------------------------------------------- + * Register access + */ + +static const uint8_t adv7511_register_defaults[] = { + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00 */ + 0x00, 0x00, 0x01, 0x0e, 0xbc, 0x18, 0x01, 0x13, + 0x25, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ + 0x46, 0x62, 0x04, 0xa8, 0x00, 0x00, 0x1c, 0x84, + 0x1c, 0xbf, 0x04, 0xa8, 0x1e, 0x70, 0x02, 0x1e, /* 20 */ + 0x00, 0x00, 0x04, 0xa8, 0x08, 0x12, 0x1b, 0xac, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb0, + 0x00, 0x50, 0x90, 0x7e, 0x79, 0x70, 0x00, 0x00, /* 40 */ + 0x00, 0xa8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, /* 50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 90 */ + 0x0b, 0x02, 0x00, 0x18, 0x5a, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x08, 0x04, 0x00, 0x00, /* a0 */ + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0 */ + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x01, 0x04, + 0x30, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, /* d0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, + 0x80, 0x75, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, /* e0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x11, 0x00, /* f0 */ + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static bool adv7511_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADV7511_REG_CHIP_REVISION: + case ADV7511_REG_SPDIF_FREQ: + case ADV7511_REG_CTS_AUTOMATIC1: + case ADV7511_REG_CTS_AUTOMATIC2: + case ADV7511_REG_VIC_DETECTED: + case ADV7511_REG_VIC_SEND: + case ADV7511_REG_AUX_VIC_DETECTED: + case ADV7511_REG_STATUS: + case ADV7511_REG_GC(1): + case ADV7511_REG_INT(0): + case ADV7511_REG_INT(1): + case ADV7511_REG_PLL_STATUS: + case ADV7511_REG_AN(0): + case ADV7511_REG_AN(1): + case ADV7511_REG_AN(2): + case ADV7511_REG_AN(3): + case ADV7511_REG_AN(4): + case ADV7511_REG_AN(5): + case ADV7511_REG_AN(6): + case ADV7511_REG_AN(7): + case ADV7511_REG_HDCP_STATUS: + case ADV7511_REG_BCAPS: + case ADV7511_REG_BKSV(0): + case ADV7511_REG_BKSV(1): + case ADV7511_REG_BKSV(2): + case ADV7511_REG_BKSV(3): + case ADV7511_REG_BKSV(4): + case ADV7511_REG_DDC_STATUS: + case ADV7511_REG_BSTATUS(0): + case ADV7511_REG_BSTATUS(1): + case ADV7511_REG_CHIP_ID_HIGH: + case ADV7511_REG_CHIP_ID_LOW: + return true; + } + + return false; +} + +static const struct regmap_config adv7511_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, + .reg_defaults_raw = adv7511_register_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(adv7511_register_defaults), + + .volatile_reg = adv7511_register_volatile, +}; + +/* ----------------------------------------------------------------------------- + * Hardware configuration + */ + +static void adv7511_set_colormap(struct adv7511 *adv7511, bool enable, + const uint16_t *coeff, + unsigned int scaling_factor) +{ + unsigned int i; + + regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1), + ADV7511_CSC_UPDATE_MODE, ADV7511_CSC_UPDATE_MODE); + + if (enable) { + for (i = 0; i < 12; ++i) { + regmap_update_bits(adv7511->regmap, + ADV7511_REG_CSC_UPPER(i), + 0x1f, coeff[i] >> 8); + regmap_write(adv7511->regmap, + ADV7511_REG_CSC_LOWER(i), + coeff[i] & 0xff); + } + } + + if (enable) + regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0), + 0xe0, 0x80 | (scaling_factor << 5)); + else + regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0), + 0x80, 0x00); + + regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1), + ADV7511_CSC_UPDATE_MODE, 0); +} + +static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0, + packet, 0xff); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, + packet, 0xff); + } + + return 0; +} + +static int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0, + packet, 0x00); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, + packet, 0x00); + } + + return 0; +} + +/* Coefficients for adv7511 color space conversion */ +static const uint16_t adv7511_csc_ycbcr_to_rgb[] = { + 0x0734, 0x04ad, 0x0000, 0x1c1b, + 0x1ddc, 0x04ad, 0x1f24, 0x0135, + 0x0000, 0x04ad, 0x087c, 0x1b77, +}; + +static void adv7511_set_config_csc(struct adv7511 *adv7511, + struct drm_connector *connector, + bool rgb) +{ + struct adv7511_video_config config; + bool output_format_422, output_format_ycbcr; + unsigned int mode; + uint8_t infoframe[17]; + + if (adv7511->edid) + config.hdmi_mode = drm_detect_hdmi_monitor(adv7511->edid); + else + config.hdmi_mode = false; + + 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 = ADV7511_CSC_SCALING_4; + config.csc_coefficents = adv7511_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 = ADV7511_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 = ADV7511_HDMI_CFG_MODE_DVI; + output_format_422 = false; + output_format_ycbcr = false; + } + + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + + adv7511_set_colormap(adv7511, config.csc_enable, + config.csc_coefficents, + config.csc_scaling_factor); + + regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x81, + (output_format_422 << 7) | output_format_ycbcr); + + regmap_update_bits(adv7511->regmap, ADV7511_REG_HDCP_HDMI_CFG, + ADV7511_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(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, + infoframe + 1, sizeof(infoframe) - 1); + + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); +} + +static void adv7511_set_link_config(struct adv7511 *adv7511, + const struct adv7511_link_config *config) +{ + /* + * The input style values documented in the datasheet don't match the + * hardware register field values :-( + */ + static const unsigned int input_styles[4] = { 0, 2, 1, 3 }; + + unsigned int clock_delay; + unsigned int color_depth; + unsigned int input_id; + + clock_delay = (config->clock_delay + 1200) / 400; + color_depth = config->input_color_depth == 8 ? 3 + : (config->input_color_depth == 10 ? 1 : 2); + + /* TODO Support input ID 6 */ + if (config->input_colorspace != HDMI_COLORSPACE_YUV422) + input_id = config->input_clock == ADV7511_INPUT_CLOCK_DDR + ? 5 : 0; + else if (config->input_clock == ADV7511_INPUT_CLOCK_DDR) + input_id = config->embedded_sync ? 8 : 7; + else if (config->input_clock == ADV7511_INPUT_CLOCK_2X) + input_id = config->embedded_sync ? 4 : 3; + else + input_id = config->embedded_sync ? 2 : 1; + + regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, 0xf, + input_id); + regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x7e, + (color_depth << 4) | + (input_styles[config->input_style] << 2)); + regmap_write(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG2, + config->input_justification << 3); + regmap_write(adv7511->regmap, ADV7511_REG_TIMING_GEN_SEQ, + config->sync_pulse << 2); + + regmap_write(adv7511->regmap, 0xba, clock_delay << 5); + + adv7511->embedded_sync = config->embedded_sync; + adv7511->hsync_polarity = config->hsync_polarity; + adv7511->vsync_polarity = config->vsync_polarity; + adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; +} + +static void adv7511_power_on(struct adv7511 *adv7511) +{ + adv7511->current_edid_segment = -1; + + regmap_write(adv7511->regmap, ADV7511_REG_INT(0), + ADV7511_INT0_EDID_READY); + regmap_write(adv7511->regmap, ADV7511_REG_INT(1), + ADV7511_INT1_DDC_ERROR); + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, + ADV7511_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 adv7511 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 enabling the output. + */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7511_REG_POWER2_HDP_SRC_MASK, + ADV7511_REG_POWER2_HDP_SRC_NONE); + + /* + * Most of the registers are reset during power down or when HPD is low. + */ + regcache_sync(adv7511->regmap); + + adv7511->powered = true; +} + +static void adv7511_power_off(struct adv7511 *adv7511) +{ + /* TODO: setup additional power down modes */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, + ADV7511_POWER_POWER_DOWN, + ADV7511_POWER_POWER_DOWN); + regcache_mark_dirty(adv7511->regmap); + + adv7511->powered = false; +} + +/* ----------------------------------------------------------------------------- + * Interrupt and hotplug detection + */ + +static bool adv7511_hpd(struct adv7511 *adv7511) +{ + unsigned int irq0; + int ret; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); + if (ret < 0) + return false; + + if (irq0 & ADV7511_INT0_HDP) { + regmap_write(adv7511->regmap, ADV7511_REG_INT(0), + ADV7511_INT0_HDP); + return true; + } + + return false; +} + +static int adv7511_irq_process(struct adv7511 *adv7511) +{ + unsigned int irq0, irq1; + int ret; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); + if (ret < 0) + return ret; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1); + if (ret < 0) + return ret; + + regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); + regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); + + if (irq0 & ADV7511_INT0_HDP) + drm_helper_hpd_irq_event(adv7511->encoder->dev); + + if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { + adv7511->edid_read = true; + + if (adv7511->i2c_main->irq) + wake_up_all(&adv7511->wq); + } + + return 0; +} + +static irqreturn_t adv7511_irq_handler(int irq, void *devid) +{ + struct adv7511 *adv7511 = devid; + int ret; + + ret = adv7511_irq_process(adv7511); + return ret < 0 ? IRQ_NONE : IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * EDID retrieval + */ + +static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout) +{ + int ret; + + if (adv7511->i2c_main->irq) { + ret = wait_event_interruptible_timeout(adv7511->wq, + adv7511->edid_read, msecs_to_jiffies(timeout)); + } else { + for (; timeout > 0; timeout -= 25) { + ret = adv7511_irq_process(adv7511); + if (ret < 0) + break; + + if (adv7511->edid_read) + break; + + msleep(25); + } + } + + return adv7511->edid_read ? 0 : -EIO; +} + +static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + struct adv7511 *adv7511 = data; + struct i2c_msg xfer[2]; + uint8_t offset; + unsigned int i; + int ret; + + if (len > 128) + return -EINVAL; + + if (adv7511->current_edid_segment != block / 2) { + unsigned int status; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS, + &status); + if (ret < 0) + return ret; + + if (status != 2) { + adv7511->edid_read = false; + regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT, + block); + ret = adv7511_wait_for_edid(adv7511, 200); + if (ret < 0) + return ret; + } + + /* Break this apart, hopefully more I2C controllers will + * support 64 byte transfers than 256 byte transfers + */ + + xfer[0].addr = adv7511->i2c_edid->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = &offset; + xfer[1].addr = adv7511->i2c_edid->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 64; + xfer[1].buf = adv7511->edid_buf; + + offset = 0; + + for (i = 0; i < 4; ++i) { + ret = i2c_transfer(adv7511->i2c_edid->adapter, xfer, + ARRAY_SIZE(xfer)); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + xfer[1].buf += 64; + offset += 64; + } + + adv7511->current_edid_segment = block / 2; + } + + if (block % 2 == 0) + memcpy(buf, adv7511->edid_buf, len); + else + memcpy(buf, adv7511->edid_buf + 128, len); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Encoder operations + */ + +static int adv7511_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + struct edid *edid; + unsigned int count; + + /* Reading the EDID only works if the device is powered */ + if (!adv7511->powered) { + regmap_write(adv7511->regmap, ADV7511_REG_INT(0), + ADV7511_INT0_EDID_READY); + regmap_write(adv7511->regmap, ADV7511_REG_INT(1), + ADV7511_INT1_DDC_ERROR); + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, + ADV7511_POWER_POWER_DOWN, 0); + adv7511->current_edid_segment = -1; + } + + edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); + + if (!adv7511->powered) + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, + ADV7511_POWER_POWER_DOWN, + ADV7511_POWER_POWER_DOWN); + + kfree(adv7511->edid); + adv7511->edid = edid; + if (!edid) + return 0; + + drm_mode_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + adv7511_set_config_csc(adv7511, connector, adv7511->rgb); + + return count; +} + +static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + if (mode == DRM_MODE_DPMS_ON) + adv7511_power_on(adv7511); + else + adv7511_power_off(adv7511); +} + +static enum drm_connector_status +adv7511_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + enum drm_connector_status status; + unsigned int val; + bool hpd; + int ret; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); + if (ret < 0) + return connector_status_disconnected; + + if (val & ADV7511_STATUS_HPD) + status = connector_status_connected; + else + status = connector_status_disconnected; + + hpd = adv7511_hpd(adv7511); + + /* 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 && adv7511->powered) { + regcache_mark_dirty(adv7511->regmap); + adv7511_power_on(adv7511); + adv7511_get_modes(encoder, connector); + if (adv7511->status == connector_status_connected) + status = connector_status_disconnected; + } else { + /* Renable HDP sensing */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7511_REG_POWER2_HDP_SRC_MASK, + ADV7511_REG_POWER2_HDP_SRC_BOTH); + } + + adv7511->status = status; + return status; +} + +static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static void adv7511_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + unsigned int low_refresh_rate; + unsigned int hsync_polarity = 0; + unsigned int vsync_polarity = 0; + + if (adv7511->embedded_sync) { + unsigned int hsync_offset, hsync_len; + unsigned int vsync_offset, vsync_len; + + hsync_offset = adj_mode->crtc_hsync_start - + adj_mode->crtc_hdisplay; + vsync_offset = adj_mode->crtc_vsync_start - + adj_mode->crtc_vdisplay; + hsync_len = adj_mode->crtc_hsync_end - + adj_mode->crtc_hsync_start; + vsync_len = adj_mode->crtc_vsync_end - + adj_mode->crtc_vsync_start; + + /* The hardware vsync generator has a off-by-one bug */ + vsync_offset += 1; + + regmap_write(adv7511->regmap, ADV7511_REG_HSYNC_PLACEMENT_MSB, + ((hsync_offset >> 10) & 0x7) << 5); + regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(0), + (hsync_offset >> 2) & 0xff); + regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(1), + ((hsync_offset & 0x3) << 6) | + ((hsync_len >> 4) & 0x3f)); + regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(2), + ((hsync_len & 0xf) << 4) | + ((vsync_offset >> 6) & 0xf)); + regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(3), + ((vsync_offset & 0x3f) << 2) | + ((vsync_len >> 8) & 0x3)); + regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(4), + vsync_len & 0xff); + + hsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PHSYNC); + vsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PVSYNC); + } else { + enum adv7511_sync_polarity mode_hsync_polarity; + enum adv7511_sync_polarity mode_vsync_polarity; + + /** + * If the input signal is always low or always high we want to + * invert or let it passthrough depending on the polarity of the + * current mode. + **/ + if (adj_mode->flags & DRM_MODE_FLAG_NHSYNC) + mode_hsync_polarity = ADV7511_SYNC_POLARITY_LOW; + else + mode_hsync_polarity = ADV7511_SYNC_POLARITY_HIGH; + + if (adj_mode->flags & DRM_MODE_FLAG_NVSYNC) + mode_vsync_polarity = ADV7511_SYNC_POLARITY_LOW; + else + mode_vsync_polarity = ADV7511_SYNC_POLARITY_HIGH; + + if (adv7511->hsync_polarity != mode_hsync_polarity && + adv7511->hsync_polarity != + ADV7511_SYNC_POLARITY_PASSTHROUGH) + hsync_polarity = 1; + + if (adv7511->vsync_polarity != mode_vsync_polarity && + adv7511->vsync_polarity != + ADV7511_SYNC_POLARITY_PASSTHROUGH) + vsync_polarity = 1; + } + + if (mode->vrefresh <= 24000) + low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ; + else if (mode->vrefresh <= 25000) + low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ; + else if (mode->vrefresh <= 30000) + low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ; + else + low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE; + + regmap_update_bits(adv7511->regmap, 0xfb, + 0x6, low_refresh_rate << 1); + regmap_update_bits(adv7511->regmap, 0x17, + 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + + /* + * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is + * supposed to give better results. + */ + + adv7511->f_tmds = mode->clock; +} + +static struct drm_encoder_slave_funcs adv7511_encoder_funcs = { + .dpms = adv7511_encoder_dpms, + .mode_valid = adv7511_encoder_mode_valid, + .mode_set = adv7511_encoder_mode_set, + .detect = adv7511_encoder_detect, + .get_modes = adv7511_get_modes, +}; + +/* ----------------------------------------------------------------------------- + * Probe & remove + */ + +static int adv7511_parse_dt(struct device_node *np, + struct adv7511_link_config *config) +{ + const char *str; + int ret; + + memset(config, 0, sizeof(*config)); + + of_property_read_u32(np, "adi,input-depth", &config->input_color_depth); + if (config->input_color_depth != 8 && config->input_color_depth != 10 && + config->input_color_depth != 12) + return -EINVAL; + + ret = of_property_read_string(np, "adi,input-colorspace", &str); + if (ret < 0) + return ret; + + if (!strcmp(str, "rgb")) + config->input_colorspace = HDMI_COLORSPACE_RGB; + else if (!strcmp(str, "yuv422")) + config->input_colorspace = HDMI_COLORSPACE_YUV422; + else if (!strcmp(str, "yuv444")) + config->input_colorspace = HDMI_COLORSPACE_YUV444; + else + return -EINVAL; + + ret = of_property_read_string(np, "adi,input-clock", &str); + if (ret < 0) + return ret; + + if (!strcmp(str, "1x")) + config->input_clock = ADV7511_INPUT_CLOCK_1X; + else if (!strcmp(str, "2x")) + config->input_clock = ADV7511_INPUT_CLOCK_2X; + else if (!strcmp(str, "ddr")) + config->input_clock = ADV7511_INPUT_CLOCK_DDR; + else + return -EINVAL; + + if (config->input_colorspace == HDMI_COLORSPACE_YUV422 || + config->input_clock != ADV7511_INPUT_CLOCK_1X) { + ret = of_property_read_u32(np, "adi,input-style", + &config->input_style); + if (ret) + return ret; + + if (config->input_style < 1 || config->input_style > 3) + return -EINVAL; + + ret = of_property_read_string(np, "adi,input-justification", + &str); + if (ret < 0) + return ret; + + if (!strcmp(str, "left")) + config->input_justification = + ADV7511_INPUT_JUSTIFICATION_LEFT; + else if (!strcmp(str, "evenly")) + config->input_justification = + ADV7511_INPUT_JUSTIFICATION_EVENLY; + else if (!strcmp(str, "right")) + config->input_justification = + ADV7511_INPUT_JUSTIFICATION_RIGHT; + else + return -EINVAL; + + } else { + config->input_style = 1; + config->input_justification = ADV7511_INPUT_JUSTIFICATION_LEFT; + } + + of_property_read_u32(np, "adi,clock-delay", &config->clock_delay); + if (config->clock_delay < -1200 || config->clock_delay > 1600) + return -EINVAL; + + config->embedded_sync = of_property_read_bool(np, "adi,embedded-sync"); + + /* Hardcode the sync pulse configurations for now. */ + config->sync_pulse = ADV7511_INPUT_SYNC_PULSE_NONE; + config->vsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH; + config->hsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH; + + return 0; +} + +static const int edid_i2c_addr = 0x7e; +static const int packet_i2c_addr = 0x70; +static const int cec_i2c_addr = 0x78; + +static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct adv7511_link_config link_config; + struct adv7511 *adv7511; + struct device *dev = &i2c->dev; + unsigned int val; + int ret; + + if (!dev->of_node) + return -EINVAL; + + adv7511 = devm_kzalloc(dev, sizeof(*adv7511), GFP_KERNEL); + if (!adv7511) + return -ENOMEM; + + adv7511->powered = false; + adv7511->status = connector_status_disconnected; + + ret = adv7511_parse_dt(dev->of_node, &link_config); + if (ret) + return ret; + + /* + * The power down GPIO is optional. If present, toggle it from active to + * inactive to wake up the encoder. + */ + adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); + if (IS_ERR(adv7511->gpio_pd)) + return PTR_ERR(adv7511->gpio_pd); + + if (adv7511->gpio_pd) { + mdelay(5); + gpiod_set_value_cansleep(adv7511->gpio_pd, 0); + } + + adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config); + if (IS_ERR(adv7511->regmap)) + return PTR_ERR(adv7511->regmap); + + ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val); + if (ret) + return ret; + dev_dbg(dev, "Rev. %d\n", val); + + ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers, + ARRAY_SIZE(adv7511_fixed_registers)); + if (ret) + return ret; + + regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); + regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, + packet_i2c_addr); + regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr); + adv7511_packet_disable(adv7511, 0xffff); + + adv7511->i2c_main = i2c; + adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); + if (!adv7511->i2c_edid) + return -ENOMEM; + + if (i2c->irq) { + init_waitqueue_head(&adv7511->wq); + + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, + adv7511_irq_handler, + IRQF_ONESHOT, dev_name(dev), + adv7511); + if (ret) + goto err_i2c_unregister_device; + } + + /* CEC is unused for now */ + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, + ADV7511_CEC_CTRL_POWER_DOWN); + + adv7511_power_off(adv7511); + + i2c_set_clientdata(i2c, adv7511); + + adv7511_set_link_config(adv7511, &link_config); + + return 0; + +err_i2c_unregister_device: + i2c_unregister_device(adv7511->i2c_edid); + + return ret; +} + +static int adv7511_remove(struct i2c_client *i2c) +{ + struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + + i2c_unregister_device(adv7511->i2c_edid); + + kfree(adv7511->edid); + + return 0; +} + +static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, + struct drm_encoder_slave *encoder) +{ + + struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + + encoder->slave_priv = adv7511; + encoder->slave_funcs = &adv7511_encoder_funcs; + + adv7511->encoder = &encoder->base; + + return 0; +} + +static const struct i2c_device_id adv7511_i2c_ids[] = { + { "adv7511", 0 }, + { "adv7511w", 0 }, + { "adv7513", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); + +static const struct of_device_id adv7511_of_ids[] = { + { .compatible = "adi,adv7511", }, + { .compatible = "adi,adv7511w", }, + { .compatible = "adi,adv7513", }, + { } +}; +MODULE_DEVICE_TABLE(of, adv7511_of_ids); + +static struct drm_i2c_encoder_driver adv7511_driver = { + .i2c_driver = { + .driver = { + .name = "adv7511", + .of_match_table = adv7511_of_ids, + }, + .id_table = adv7511_i2c_ids, + .probe = adv7511_probe, + .remove = adv7511_remove, + }, + + .encoder_init = adv7511_encoder_init, +}; + +static int __init adv7511_init(void) +{ + return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver); +} +module_init(adv7511_init); + +static void __exit adv7511_exit(void) +{ + drm_i2c_encoder_unregister(&adv7511_driver); +} +module_exit(adv7511_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h new file mode 100644 index 00000000000000..6599ed538426d6 --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7511.h @@ -0,0 +1,289 @@ +/* + * Analog Devices ADV7511 HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __DRM_I2C_ADV7511_H__ +#define __DRM_I2C_ADV7511_H__ + +#include + +#define ADV7511_REG_CHIP_REVISION 0x00 +#define ADV7511_REG_N0 0x01 +#define ADV7511_REG_N1 0x02 +#define ADV7511_REG_N2 0x03 +#define ADV7511_REG_SPDIF_FREQ 0x04 +#define ADV7511_REG_CTS_AUTOMATIC1 0x05 +#define ADV7511_REG_CTS_AUTOMATIC2 0x06 +#define ADV7511_REG_CTS_MANUAL0 0x07 +#define ADV7511_REG_CTS_MANUAL1 0x08 +#define ADV7511_REG_CTS_MANUAL2 0x09 +#define ADV7511_REG_AUDIO_SOURCE 0x0a +#define ADV7511_REG_AUDIO_CONFIG 0x0b +#define ADV7511_REG_I2S_CONFIG 0x0c +#define ADV7511_REG_I2S_WIDTH 0x0d +#define ADV7511_REG_AUDIO_SUB_SRC0 0x0e +#define ADV7511_REG_AUDIO_SUB_SRC1 0x0f +#define ADV7511_REG_AUDIO_SUB_SRC2 0x10 +#define ADV7511_REG_AUDIO_SUB_SRC3 0x11 +#define ADV7511_REG_AUDIO_CFG1 0x12 +#define ADV7511_REG_AUDIO_CFG2 0x13 +#define ADV7511_REG_AUDIO_CFG3 0x14 +#define ADV7511_REG_I2C_FREQ_ID_CFG 0x15 +#define ADV7511_REG_VIDEO_INPUT_CFG1 0x16 +#define ADV7511_REG_CSC_UPPER(x) (0x18 + (x) * 2) +#define ADV7511_REG_CSC_LOWER(x) (0x19 + (x) * 2) +#define ADV7511_REG_SYNC_DECODER(x) (0x30 + (x)) +#define ADV7511_REG_DE_GENERATOR (0x35 + (x)) +#define ADV7511_REG_PIXEL_REPETITION 0x3b +#define ADV7511_REG_VIC_MANUAL 0x3c +#define ADV7511_REG_VIC_SEND 0x3d +#define ADV7511_REG_VIC_DETECTED 0x3e +#define ADV7511_REG_AUX_VIC_DETECTED 0x3f +#define ADV7511_REG_PACKET_ENABLE0 0x40 +#define ADV7511_REG_POWER 0x41 +#define ADV7511_REG_STATUS 0x42 +#define ADV7511_REG_EDID_I2C_ADDR 0x43 +#define ADV7511_REG_PACKET_ENABLE1 0x44 +#define ADV7511_REG_PACKET_I2C_ADDR 0x45 +#define ADV7511_REG_DSD_ENABLE 0x46 +#define ADV7511_REG_VIDEO_INPUT_CFG2 0x48 +#define ADV7511_REG_INFOFRAME_UPDATE 0x4a +#define ADV7511_REG_GC(x) (0x4b + (x)) /* 0x4b - 0x51 */ +#define ADV7511_REG_AVI_INFOFRAME_VERSION 0x52 +#define ADV7511_REG_AVI_INFOFRAME_LENGTH 0x53 +#define ADV7511_REG_AVI_INFOFRAME_CHECKSUM 0x54 +#define ADV7511_REG_AVI_INFOFRAME(x) (0x55 + (x)) /* 0x55 - 0x6f */ +#define ADV7511_REG_AUDIO_INFOFRAME_VERSION 0x70 +#define ADV7511_REG_AUDIO_INFOFRAME_LENGTH 0x71 +#define ADV7511_REG_AUDIO_INFOFRAME_CHECKSUM 0x72 +#define ADV7511_REG_AUDIO_INFOFRAME(x) (0x73 + (x)) /* 0x73 - 0x7c */ +#define ADV7511_REG_INT_ENABLE(x) (0x94 + (x)) +#define ADV7511_REG_INT(x) (0x96 + (x)) +#define ADV7511_REG_INPUT_CLK_DIV 0x9d +#define ADV7511_REG_PLL_STATUS 0x9e +#define ADV7511_REG_HDMI_POWER 0xa1 +#define ADV7511_REG_HDCP_HDMI_CFG 0xaf +#define ADV7511_REG_AN(x) (0xb0 + (x)) /* 0xb0 - 0xb7 */ +#define ADV7511_REG_HDCP_STATUS 0xb8 +#define ADV7511_REG_BCAPS 0xbe +#define ADV7511_REG_BKSV(x) (0xc0 + (x)) /* 0xc0 - 0xc3 */ +#define ADV7511_REG_EDID_SEGMENT 0xc4 +#define ADV7511_REG_DDC_STATUS 0xc8 +#define ADV7511_REG_EDID_READ_CTRL 0xc9 +#define ADV7511_REG_BSTATUS(x) (0xca + (x)) /* 0xca - 0xcb */ +#define ADV7511_REG_TIMING_GEN_SEQ 0xd0 +#define ADV7511_REG_POWER2 0xd6 +#define ADV7511_REG_HSYNC_PLACEMENT_MSB 0xfa + +#define ADV7511_REG_SYNC_ADJUSTMENT(x) (0xd7 + (x)) /* 0xd7 - 0xdc */ +#define ADV7511_REG_TMDS_CLOCK_INV 0xde +#define ADV7511_REG_ARC_CTRL 0xdf +#define ADV7511_REG_CEC_I2C_ADDR 0xe1 +#define ADV7511_REG_CEC_CTRL 0xe2 +#define ADV7511_REG_CHIP_ID_HIGH 0xf5 +#define ADV7511_REG_CHIP_ID_LOW 0xf6 + +#define ADV7511_CSC_ENABLE BIT(7) +#define ADV7511_CSC_UPDATE_MODE BIT(5) + +#define ADV7511_INT0_HDP BIT(7) +#define ADV7511_INT0_VSYNC BIT(5) +#define ADV7511_INT0_AUDIO_FIFO_FULL BIT(4) +#define ADV7511_INT0_EDID_READY BIT(2) +#define ADV7511_INT0_HDCP_AUTHENTICATED BIT(1) + +#define ADV7511_INT1_DDC_ERROR BIT(7) +#define ADV7511_INT1_BKSV BIT(6) +#define ADV7511_INT1_CEC_TX_READY BIT(5) +#define ADV7511_INT1_CEC_TX_ARBIT_LOST BIT(4) +#define ADV7511_INT1_CEC_TX_RETRY_TIMEOUT BIT(3) +#define ADV7511_INT1_CEC_RX_READY3 BIT(2) +#define ADV7511_INT1_CEC_RX_READY2 BIT(1) +#define ADV7511_INT1_CEC_RX_READY1 BIT(0) + +#define ADV7511_ARC_CTRL_POWER_DOWN BIT(0) + +#define ADV7511_CEC_CTRL_POWER_DOWN BIT(0) + +#define ADV7511_POWER_POWER_DOWN BIT(6) + +#define ADV7511_HDMI_CFG_MODE_MASK 0x2 +#define ADV7511_HDMI_CFG_MODE_DVI 0x0 +#define ADV7511_HDMI_CFG_MODE_HDMI 0x2 + +#define ADV7511_AUDIO_SELECT_I2C 0x0 +#define ADV7511_AUDIO_SELECT_SPDIF 0x1 +#define ADV7511_AUDIO_SELECT_DSD 0x2 +#define ADV7511_AUDIO_SELECT_HBR 0x3 +#define ADV7511_AUDIO_SELECT_DST 0x4 + +#define ADV7511_I2S_SAMPLE_LEN_16 0x2 +#define ADV7511_I2S_SAMPLE_LEN_20 0x3 +#define ADV7511_I2S_SAMPLE_LEN_18 0x4 +#define ADV7511_I2S_SAMPLE_LEN_22 0x5 +#define ADV7511_I2S_SAMPLE_LEN_19 0x8 +#define ADV7511_I2S_SAMPLE_LEN_23 0x9 +#define ADV7511_I2S_SAMPLE_LEN_24 0xb +#define ADV7511_I2S_SAMPLE_LEN_17 0xc +#define ADV7511_I2S_SAMPLE_LEN_21 0xd + +#define ADV7511_SAMPLE_FREQ_44100 0x0 +#define ADV7511_SAMPLE_FREQ_48000 0x2 +#define ADV7511_SAMPLE_FREQ_32000 0x3 +#define ADV7511_SAMPLE_FREQ_88200 0x8 +#define ADV7511_SAMPLE_FREQ_96000 0xa +#define ADV7511_SAMPLE_FREQ_176400 0xc +#define ADV7511_SAMPLE_FREQ_192000 0xe + +#define ADV7511_STATUS_POWER_DOWN_POLARITY BIT(7) +#define ADV7511_STATUS_HPD BIT(6) +#define ADV7511_STATUS_MONITOR_SENSE BIT(5) +#define ADV7511_STATUS_I2S_32BIT_MODE BIT(3) + +#define ADV7511_PACKET_ENABLE_N_CTS BIT(8+6) +#define ADV7511_PACKET_ENABLE_AUDIO_SAMPLE BIT(8+5) +#define ADV7511_PACKET_ENABLE_AVI_INFOFRAME BIT(8+4) +#define ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME BIT(8+3) +#define ADV7511_PACKET_ENABLE_GC BIT(7) +#define ADV7511_PACKET_ENABLE_SPD BIT(6) +#define ADV7511_PACKET_ENABLE_MPEG BIT(5) +#define ADV7511_PACKET_ENABLE_ACP BIT(4) +#define ADV7511_PACKET_ENABLE_ISRC BIT(3) +#define ADV7511_PACKET_ENABLE_GM BIT(2) +#define ADV7511_PACKET_ENABLE_SPARE2 BIT(1) +#define ADV7511_PACKET_ENABLE_SPARE1 BIT(0) + +#define ADV7511_REG_POWER2_HDP_SRC_MASK 0xc0 +#define ADV7511_REG_POWER2_HDP_SRC_BOTH 0x00 +#define ADV7511_REG_POWER2_HDP_SRC_HDP 0x40 +#define ADV7511_REG_POWER2_HDP_SRC_CEC 0x80 +#define ADV7511_REG_POWER2_HDP_SRC_NONE 0xc0 +#define ADV7511_REG_POWER2_TDMS_ENABLE BIT(4) +#define ADV7511_REG_POWER2_GATE_INPUT_CLK BIT(0) + +#define ADV7511_LOW_REFRESH_RATE_NONE 0x0 +#define ADV7511_LOW_REFRESH_RATE_24HZ 0x1 +#define ADV7511_LOW_REFRESH_RATE_25HZ 0x2 +#define ADV7511_LOW_REFRESH_RATE_30HZ 0x3 + +#define ADV7511_AUDIO_CFG3_LEN_MASK 0x0f +#define ADV7511_I2C_FREQ_ID_CFG_RATE_MASK 0xf0 + +#define ADV7511_AUDIO_SOURCE_I2S 0 +#define ADV7511_AUDIO_SOURCE_SPDIF 1 + +#define ADV7511_I2S_FORMAT_I2S 0 +#define ADV7511_I2S_FORMAT_RIGHT_J 1 +#define ADV7511_I2S_FORMAT_LEFT_J 2 + +#define ADV7511_PACKET(p, x) ((p) * 0x20 + (x)) +#define ADV7511_PACKET_SDP(x) ADV7511_PACKET(0, x) +#define ADV7511_PACKET_MPEG(x) ADV7511_PACKET(1, x) +#define ADV7511_PACKET_ACP(x) ADV7511_PACKET(2, x) +#define ADV7511_PACKET_ISRC1(x) ADV7511_PACKET(3, x) +#define ADV7511_PACKET_ISRC2(x) ADV7511_PACKET(4, x) +#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x) +#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x) + +enum adv7511_input_clock { + ADV7511_INPUT_CLOCK_1X, + ADV7511_INPUT_CLOCK_2X, + ADV7511_INPUT_CLOCK_DDR, +}; + +enum adv7511_input_justification { + ADV7511_INPUT_JUSTIFICATION_EVENLY = 0, + ADV7511_INPUT_JUSTIFICATION_RIGHT = 1, + ADV7511_INPUT_JUSTIFICATION_LEFT = 2, +}; + +enum adv7511_input_sync_pulse { + ADV7511_INPUT_SYNC_PULSE_DE = 0, + ADV7511_INPUT_SYNC_PULSE_HSYNC = 1, + ADV7511_INPUT_SYNC_PULSE_VSYNC = 2, + ADV7511_INPUT_SYNC_PULSE_NONE = 3, +}; + +/** + * enum adv7511_sync_polarity - Polarity for the input sync signals + * @ADV7511_SYNC_POLARITY_PASSTHROUGH: Sync polarity matches that of + * the currently configured mode. + * @ADV7511_SYNC_POLARITY_LOW: Sync polarity is low + * @ADV7511_SYNC_POLARITY_HIGH: Sync polarity is high + * + * If the polarity is set to either LOW or HIGH the driver will configure the + * ADV7511 to internally invert the sync signal if required to match the sync + * polarity setting for the currently selected output mode. + * + * If the polarity is set to PASSTHROUGH, the ADV7511 will route the signal + * unchanged. This is used when the upstream graphics core already generates + * the sync signals with the correct polarity. + */ +enum adv7511_sync_polarity { + ADV7511_SYNC_POLARITY_PASSTHROUGH, + ADV7511_SYNC_POLARITY_LOW, + ADV7511_SYNC_POLARITY_HIGH, +}; + +/** + * struct adv7511_link_config - Describes adv7511 hardware configuration + * @input_color_depth: Number of bits per color component (8, 10 or 12) + * @input_colorspace: The input colorspace (RGB, YUV444, YUV422) + * @input_clock: The input video clock style (1x, 2x, DDR) + * @input_style: The input component arrangement variant + * @input_justification: Video input format bit justification + * @clock_delay: Clock delay for the input clock (in ps) + * @embedded_sync: Video input uses BT.656-style embedded sync + * @sync_pulse: Select the sync pulse + * @vsync_polarity: vsync input signal configuration + * @hsync_polarity: hsync input signal configuration + */ +struct adv7511_link_config { + unsigned int input_color_depth; + enum hdmi_colorspace input_colorspace; + enum adv7511_input_clock input_clock; + unsigned int input_style; + enum adv7511_input_justification input_justification; + + int clock_delay; + + bool embedded_sync; + enum adv7511_input_sync_pulse sync_pulse; + enum adv7511_sync_polarity vsync_polarity; + enum adv7511_sync_polarity hsync_polarity; +}; + +/** + * enum adv7511_csc_scaling - Scaling factor for the ADV7511 CSC + * @ADV7511_CSC_SCALING_1: CSC results are not scaled + * @ADV7511_CSC_SCALING_2: CSC results are scaled by a factor of two + * @ADV7511_CSC_SCALING_4: CSC results are scalled by a factor of four + */ +enum adv7511_csc_scaling { + ADV7511_CSC_SCALING_1 = 0, + ADV7511_CSC_SCALING_2 = 1, + ADV7511_CSC_SCALING_4 = 2, +}; + +/** + * struct adv7511_video_config - Describes adv7511 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 adv7511_video_config { + bool csc_enable; + enum adv7511_csc_scaling csc_scaling_factor; + const uint16_t *csc_coefficents; + + bool hdmi_mode; + struct hdmi_avi_infoframe avi_infoframe; +}; + +#endif /* __DRM_I2C_ADV7511_H__ */ From 7ff8a2dc519b0107bd23c0c498229335eabf35ee Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 16 Jun 2015 13:23:07 +0800 Subject: [PATCH 05/28] align mainline basis adv7511 Signed-off-by: Andy Green --- drivers/gpu/drm/i2c/Kconfig | 40 ++ drivers/gpu/drm/i2c/adv7511.c | 798 ++++++++++++++++++++++++++++------ drivers/gpu/drm/i2c/adv7511.h | 61 +++ 3 files changed, 770 insertions(+), 129 deletions(-) diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index f96263052897ea..b44c15ab4f33d8 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -7,6 +7,46 @@ config DRM_I2C_ADV7511 help Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. +config DRM_I2C_ADV7511_SLAVE_ENCODER + tristate "Use ADV7511 as an I2C slave encoder" + default y + depends on DRM_I2C_ADV7511 + help + Configure ADV7511 as an I2C slave encoder. Selecting 'n' will + configure the driver as a drm bridge driver + +config DRM_I2C_ADV7511_SLAVE_ENCODER + tristate "Use ADV7511 as an I2C slave encoder" + default y + depends on DRM_I2C_ADV7511 + help + Configure ADV7511 as an I2C slave encoder. Selecting 'n' will + configure the driver as a drm bridge driver + +config DRM_I2C_ADV7511_SLAVE_ENCODER + tristate "Use ADV7511 as an I2C slave encoder" + default y + depends on DRM_I2C_ADV7511 + help + Configure ADV7511 as an I2C slave encoder. Selecting 'n' will + configure the driver as a drm bridge driver + +config DRM_I2C_ADV7511_SLAVE_ENCODER + tristate "Use ADV7511 as an I2C slave encoder" + default y + depends on DRM_I2C_ADV7511 + help + Configure ADV7511 as an I2C slave encoder. Selecting 'n' will + configure the driver as a drm bridge driver + +config DRM_I2C_ADV7511_SLAVE_ENCODER + tristate "Use ADV7511 as an I2C slave encoder" + default y + depends on DRM_I2C_ADV7511 + help + Configure ADV7511 as an I2C slave encoder. Selecting 'n' will + configure the driver as a drm bridge driver + config DRM_I2C_CH7006 tristate "Chrontel ch7006 TV encoder" default m if DRM_NOUVEAU diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index b728523e194f75..74f8bd39ba2dd0 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -17,41 +17,15 @@ #include #include #include +//#include +//#include +#include +#include #include "adv7511.h" -struct adv7511 { - struct i2c_client *i2c_main; - struct i2c_client *i2c_edid; - - struct regmap *regmap; - struct regmap *packet_memory_regmap; - enum drm_connector_status status; - bool powered; - - unsigned int f_tmds; - - unsigned int current_edid_segment; - uint8_t edid_buf[256]; - bool edid_read; - - wait_queue_head_t wq; - struct drm_encoder *encoder; - - bool embedded_sync; - enum adv7511_sync_polarity vsync_polarity; - enum adv7511_sync_polarity hsync_polarity; - bool rgb; - - struct edid *edid; - - struct gpio_desc *gpio_pd; -}; - -static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) -{ - return to_encoder_slave(encoder)->slave_priv; -} +/* uncomment to enable Internal Timing Generator + DE */ +//#define ITG /* ADI recommended values for proper operation. */ static const struct reg_default adv7511_fixed_registers[] = { @@ -66,6 +40,24 @@ static const struct reg_default adv7511_fixed_registers[] = { { 0x55, 0x02 }, }; +/* 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_cec_fixed_registers[] = { + { 0x15, 0xd0 }, + { 0x17, 0xd0 }, + { 0x24, 0x20 }, + { 0x57, 0x11 }, + { 0x05, 0xc8 }, +}; + /* ----------------------------------------------------------------------------- * Register access */ @@ -158,6 +150,15 @@ static const struct regmap_config adv7511_regmap_config = { .volatile_reg = adv7511_register_volatile, }; +static const struct regmap_config adv7533_cec_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +}; + + /* ----------------------------------------------------------------------------- * Hardware configuration */ @@ -193,7 +194,7 @@ static void adv7511_set_colormap(struct adv7511 *adv7511, bool enable, ADV7511_CSC_UPDATE_MODE, 0); } -static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet) +int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet) { if (packet & 0xff) regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0, @@ -208,7 +209,7 @@ static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet) return 0; } -static int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet) +int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet) { if (packet & 0xff) regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0, @@ -325,6 +326,17 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, unsigned int color_depth; unsigned int input_id; + adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; + + /* + * TODO: some of the below configurations might be needed for ADV7533 + * too, check in ADV7533 spec. + */ + if (adv7511->type == ADV7533) { + adv7511->num_dsi_lanes = config->num_dsi_lanes; + return; + } + clock_delay = (config->clock_delay + 1200) / 400; color_depth = config->input_color_depth == 8 ? 3 : (config->input_color_depth == 10 ? 1 : 2); @@ -355,17 +367,130 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, adv7511->embedded_sync = config->embedded_sync; adv7511->hsync_polarity = config->hsync_polarity; adv7511->vsync_polarity = config->vsync_polarity; - adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; +} + +static void adv7511_dsi_config_tgen(struct adv7511 *adv7511) +{ + struct drm_display_mode *mode = adv7511->curr_mode; + unsigned int hsw, hfp, hbp, vsw, vfp, vbp, hd, vd; + + hsw = mode->hsync_end - mode->hsync_start; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + vsw = mode->vsync_end - mode->vsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + hd = hsw + hbp - 1; + vd = vsw + vbp; + + /* set pixel clock divider mode to auto */ + regmap_write(adv7511->regmap_cec, 0x16, 0x00); + + pr_info("%s: ITG HTOT=%d, HSA=%d, HFP=%d, HBP=%d\n", + __func__, mode->htotal, hsw, hfp, hbp); + pr_info("%s: ITG VTOT=%d, VSA=%d, VFP=%d, VBP=%d\n", + __func__, mode->vtotal, vsw, vfp, vbp); + + /* horizontal porch params */ + regmap_write(adv7511->regmap_cec, 0x28, mode->htotal >> 4); + regmap_write(adv7511->regmap_cec, 0x29, (mode->htotal << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x2a, hsw >> 4); + regmap_write(adv7511->regmap_cec, 0x2b, (hsw << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x2c, hfp >> 4); + regmap_write(adv7511->regmap_cec, 0x2d, (hfp << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x2e, hbp >> 4); + regmap_write(adv7511->regmap_cec, 0x2f, (hbp << 4) & 0xff); + + /* vertical porch params */ + regmap_write(adv7511->regmap_cec, 0x30, mode->vtotal >> 4); + regmap_write(adv7511->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x32, vsw >> 4); + regmap_write(adv7511->regmap_cec, 0x33, (vsw << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x34, vfp >> 4); + regmap_write(adv7511->regmap_cec, 0x35, (vfp << 4) & 0xff); + regmap_write(adv7511->regmap_cec, 0x36, vbp >> 4); + regmap_write(adv7511->regmap_cec, 0x37, (vbp << 4) & 0xff); + + /* DE generator */ + + pr_info("%s: DE HSDLY=%d, VSDLY=%d, HACT=%d, VACT=%d\n", + __func__, hd, vd, mode->hdisplay, mode->vdisplay); + + + /* DE generation HSYNC delay */ + regmap_write(adv7511->regmap, 0x35, hd >> 2); + regmap_write(adv7511->regmap, 0x36, (hd << 6) | (vd >> 3)); + /* DE generation VSYNC delay */ + regmap_write(adv7511->regmap, 0x37, (vd << 5) | + (mode->hdisplay >> 7)); + regmap_write(adv7511->regmap, 0x38, (mode->hdisplay << 1)); + regmap_write(adv7511->regmap, 0x39, (mode->vdisplay >> 4)); + regmap_write(adv7511->regmap, 0x3a, (mode->vdisplay << 4)); + + + regmap_write(adv7511->regmap, 0xd0, BIT(1)); + + pr_info("%s: DE HSPLA=%d, VSPLA=%d\n", + __func__, hfp, vfp); + + + /* hsync placement relative to DE end */ + regmap_write(adv7511->regmap, 0xd7, (hfp >> 2)); + regmap_write(adv7511->regmap, 0xd8, (hfp << 6) | + (hsw >> 4)); + regmap_write(adv7511->regmap, 0xd9, (hsw << 4) | + /* vsync placement relative to DE end */ + (vfp >> 6)); + regmap_write(adv7511->regmap, 0xda, (vfp << 2) | + (vsw >> 8)); + regmap_write(adv7511->regmap, 0xdb, vsw); + +#ifdef ITG + regmap_update_bits(adv7511->regmap, 0x17, BIT(0), BIT(0)); + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, BIT(1), BIT(1)); +#endif +} + +static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) +{ + if (adv7511->type != ADV7533) + return; + + if (adv7511->powered) { + adv7511_dsi_config_tgen(adv7511); + +#ifdef ITG + /* reset internal timing generator */ + regmap_write(adv7511->regmap_cec, 0x27, 0xcb); + regmap_write(adv7511->regmap_cec, 0x27, 0x8b); + regmap_write(adv7511->regmap_cec, 0x27, 0xcb); +#else + regmap_write(adv7511->regmap_cec, 0x27, 0x0b); +#endif + + /* enable hdmi */ + regmap_write(adv7511->regmap_cec, 0x03, 0x89); + + /* explicitly disable test mode */ + regmap_write(adv7511->regmap_cec, 0x55, 0x00); + /* deassert DSI reset */ + regmap_update_bits(adv7511->regmap, 0x26, BIT(5), 0); + /* Black OFF */ + regmap_write(adv7511->regmap, 0xd5, 0); + } else { + /* Black ON */ + regmap_write(adv7511->regmap, 0xd5, 1); + regmap_write(adv7511->regmap_cec, 0x03, 0x0b); + regmap_write(adv7511->regmap_cec, 0x27, 0x0b); + /* assert DSI reset */ + regmap_update_bits(adv7511->regmap, 0x26, BIT(5), BIT(5)); + } } static void adv7511_power_on(struct adv7511 *adv7511) { adv7511->current_edid_segment = -1; - regmap_write(adv7511->regmap, ADV7511_REG_INT(0), - ADV7511_INT0_EDID_READY); - regmap_write(adv7511->regmap, ADV7511_REG_INT(1), - ADV7511_INT1_DDC_ERROR); regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, ADV7511_POWER_POWER_DOWN, 0); @@ -387,6 +512,8 @@ static void adv7511_power_on(struct adv7511 *adv7511) regcache_sync(adv7511->regmap); adv7511->powered = true; + + adv7511_dsi_receiver_dpms(adv7511); } static void adv7511_power_off(struct adv7511 *adv7511) @@ -398,6 +525,8 @@ static void adv7511_power_off(struct adv7511 *adv7511) regcache_mark_dirty(adv7511->regmap); adv7511->powered = false; + + adv7511_dsi_receiver_dpms(adv7511); } /* ----------------------------------------------------------------------------- @@ -438,13 +567,13 @@ static int adv7511_irq_process(struct adv7511 *adv7511) regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); - if (irq0 & ADV7511_INT0_HDP) + if (adv7511->encoder && (irq0 & ADV7511_INT0_HDP)) drm_helper_hpd_irq_event(adv7511->encoder->dev); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; - if (adv7511->i2c_main->irq) + if (adv7511->irq) wake_up_all(&adv7511->wq); } @@ -464,29 +593,6 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) * EDID retrieval */ -static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout) -{ - int ret; - - if (adv7511->i2c_main->irq) { - ret = wait_event_interruptible_timeout(adv7511->wq, - adv7511->edid_read, msecs_to_jiffies(timeout)); - } else { - for (; timeout > 0; timeout -= 25) { - ret = adv7511_irq_process(adv7511); - if (ret < 0) - break; - - if (adv7511->edid_read) - break; - - msleep(25); - } - } - - return adv7511->edid_read ? 0 : -EIO; -} - static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) { @@ -495,6 +601,7 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, uint8_t offset; unsigned int i; int ret; + int timeout = 100; if (len > 128) return -EINVAL; @@ -502,20 +609,24 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, if (adv7511->current_edid_segment != block / 2) { unsigned int status; - ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS, - &status); - if (ret < 0) - return ret; + adv7511->edid_read = false; - if (status != 2) { - adv7511->edid_read = false; - regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT, - block); - ret = adv7511_wait_for_edid(adv7511, 200); - if (ret < 0) + while (--timeout) { + ret = regmap_read(adv7511->regmap, + ADV7511_REG_DDC_STATUS, &status); + if (ret < 0) { + pr_err("a\n"); return ret; + } + if (status == 2) + break; + + msleep(10); } + if (!timeout) + return -ETIMEDOUT; + /* Break this apart, hopefully more I2C controllers will * support 64 byte transfers than 256 byte transfers */ @@ -536,6 +647,7 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, ARRAY_SIZE(xfer)); if (ret < 0) return ret; + else if (ret != 2) return -EIO; @@ -555,18 +667,19 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, } /* ----------------------------------------------------------------------------- - * Encoder operations + * ADV75xx helpers */ - -static int adv7511_get_modes(struct drm_encoder *encoder, - struct drm_connector *connector) +static int adv7511_get_modes(struct adv7511 *adv7511, + struct drm_connector *connector) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); struct edid *edid; unsigned int count; /* Reading the EDID only works if the device is powered */ if (!adv7511->powered) { + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7511_REG_POWER2_HDP_SRC_MASK, + ADV7511_REG_POWER2_HDP_SRC_NONE); regmap_write(adv7511->regmap, ADV7511_REG_INT(0), ADV7511_INT0_EDID_READY); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), @@ -596,21 +709,10 @@ static int adv7511_get_modes(struct drm_encoder *encoder, return count; } -static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); - - if (mode == DRM_MODE_DPMS_ON) - adv7511_power_on(adv7511); - else - adv7511_power_off(adv7511); -} - static enum drm_connector_status -adv7511_encoder_detect(struct drm_encoder *encoder, +adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); enum drm_connector_status status; unsigned int val; bool hpd; @@ -634,7 +736,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder, if (status == connector_status_connected && hpd && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); - adv7511_get_modes(encoder, connector); + adv7511_get_modes(adv7511, connector); if (adv7511->status == connector_status_connected) status = connector_status_disconnected; } else { @@ -648,23 +750,17 @@ adv7511_encoder_detect(struct drm_encoder *encoder, return status; } -static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - if (mode->clock > 165000) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - -static void adv7511_encoder_mode_set(struct drm_encoder *encoder, +static void adv7511_mode_set(struct adv7511 *adv7511, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + static u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */ unsigned int low_refresh_rate; unsigned int hsync_polarity = 0; unsigned int vsync_polarity = 0; + int lanes = 3; + + regmap_write(adv7511->regmap, 0xd5, 1); if (adv7511->embedded_sync) { unsigned int hsync_offset, hsync_len; @@ -730,6 +826,15 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, vsync_polarity = 1; } + if (!adv7511->num_dsi_lanes) { + /* if not fixed in dts, we decide it dynamically */ + if (adj_mode->clock > 115000) + lanes = 4; + regmap_write(adv7511->regmap_cec, 0x1C, lanes << 4); + regmap_write(adv7511->regmap_cec, 0x16, + clock_div_by_lanes[lanes - 2] << 3); + } + if (mode->vrefresh <= 24000) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ; else if (mode->vrefresh <= 25000) @@ -744,12 +849,76 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, regmap_update_bits(adv7511->regmap, 0x17, 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + adv7511->curr_mode = adj_mode; /* * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is * supposed to give better results. */ adv7511->f_tmds = mode->clock; + + regmap_update_bits(adv7511->regmap, 0xaf, BIT(1), BIT(1) * ( + (mode->hdisplay == 1920 && mode->vdisplay == 1080) || + (mode->hdisplay == 1280 && mode->vdisplay == 720) || + (mode->hdisplay == 1280 && mode->vdisplay == 576) || + (mode->hdisplay == 720 && mode->vdisplay == 576) || + (mode->hdisplay == 720 && mode->vdisplay == 480) + )); +} + +#ifdef CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER +/* ----------------------------------------------------------------------------- + * Encoder i2c slave functions + */ + +static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) +{ + return to_encoder_slave(encoder)->slave_priv; +} + +static int adv7511_encoder_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + return adv7511_get_modes(adv7511, connector); +} + +static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + if (mode == DRM_MODE_DPMS_ON) + adv7511_power_on(adv7511); + else + adv7511_power_off(adv7511); +} + +static enum drm_connector_status +adv7511_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + return adv7511_detect(adv7511, connector); +} + +static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static void adv7511_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + adv7511_mode_set(adv7511, mode, adj_mode); } static struct drm_encoder_slave_funcs adv7511_encoder_funcs = { @@ -757,21 +926,142 @@ static struct drm_encoder_slave_funcs adv7511_encoder_funcs = { .mode_valid = adv7511_encoder_mode_valid, .mode_set = adv7511_encoder_mode_set, .detect = adv7511_encoder_detect, - .get_modes = adv7511_get_modes, + .get_modes = adv7511_encoder_get_modes, +}; +#else +/* ----------------------------------------------------------------------------- + * Bridge and connector functions + */ + +static struct adv7511 *connector_to_adv7511(struct drm_connector *connector) +{ + return container_of(connector, struct adv7511, connector); +} + +/* connector helper functions */ +static int adv7511_connector_get_modes(struct drm_connector *connector) +{ + struct adv7511 *adv7511 = connector_to_adv7511(connector); + + return adv7511_get_modes(adv7511, connector); +} + +static struct drm_encoder *adv7511_connector_best_encoder(struct drm_connector *connector) +{ + struct adv7511 *adv7511 = connector_to_adv7511(connector); + + return adv7511->bridge.encoder; +} + +static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = { + .get_modes = adv7511_connector_get_modes, + .best_encoder = adv7511_connector_best_encoder, +}; + +static enum drm_connector_status +adv7511_connector_detect(struct drm_connector *connector, bool force) +{ + struct adv7511 *adv7511 = connector_to_adv7511(connector); + + return adv7511_detect(adv7511, connector); +} + +static struct drm_connector_funcs adv7511_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = adv7511_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +/* bridge funcs */ +static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +{ + return container_of(bridge, struct adv7511, bridge); +} + +static void adv7511_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + adv7511_power_on(adv7511); +} + +static void adv7511_bridge_post_disable(struct drm_bridge *bridge) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + adv7511_power_off(adv7511); +} + +static void adv7511_bridge_enable(struct drm_bridge *bridge) +{ +} + +static void adv7511_bridge_disable(struct drm_bridge *bridge) +{ +} + +static void adv7511_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + adv7511_mode_set(adv7511, mode, adj_mode); +} + +static int adv7511_bridge_attach(struct drm_bridge *bridge) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + int ret; + + adv7511->encoder = bridge->encoder; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + adv7511->connector.polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(bridge->dev, &adv7511->connector, + &adv7511_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&adv7511->connector, + &adv7511_connector_helper_funcs); + drm_connector_register(&adv7511->connector); + drm_mode_connector_attach_encoder(&adv7511->connector, adv7511->encoder); + + drm_helper_hpd_irq_event(adv7511->connector.dev); + + return ret; +} + +static struct drm_bridge_funcs adv7511_bridge_funcs = { + .pre_enable = adv7511_bridge_pre_enable, + .enable = adv7511_bridge_enable, + .disable = adv7511_bridge_disable, + .post_disable = adv7511_bridge_post_disable, + .attach = adv7511_bridge_attach, + .mode_set = adv7511_bridge_mode_set, +}; +#endif + /* ----------------------------------------------------------------------------- * Probe & remove */ -static int adv7511_parse_dt(struct device_node *np, +static int adv7511_parse_dt(struct adv7511 *adv7511, struct device_node *np, struct adv7511_link_config *config) { const char *str; int ret; - memset(config, 0, sizeof(*config)); - of_property_read_u32(np, "adi,input-depth", &config->input_color_depth); if (config->input_color_depth != 8 && config->input_color_depth != 10 && config->input_color_depth != 12) @@ -790,6 +1080,20 @@ static int adv7511_parse_dt(struct device_node *np, else return -EINVAL; + if (adv7511->type == ADV7533) { + /* + * Optional... leaving it out uses a heuristic to determine + * dynamically + */ + if (!of_property_read_u32(np, "adi,dsi-lanes", + &config->num_dsi_lanes)) + if (config->num_dsi_lanes < 1 || + config->num_dsi_lanes > 4) + return -EINVAL; + + return 0; + } + ret = of_property_read_string(np, "adi,input-clock", &str); if (ret < 0) return ret; @@ -852,7 +1156,18 @@ static int adv7511_parse_dt(struct device_node *np, static const int edid_i2c_addr = 0x7e; static const int packet_i2c_addr = 0x70; static const int cec_i2c_addr = 0x78; +static const int main_i2c_addr = 0x72; +static const struct of_device_id adv7511_of_ids[] = { + { .compatible = "adi,adv7511", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7511w", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7513", .data= (void *)ADV7511 }, + { .compatible = "adi,adv7533", .data = (void *)ADV7533 }, + { } +}; +MODULE_DEVICE_TABLE(of, adv7511_of_ids); + +#ifdef CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct adv7511_link_config link_config; @@ -871,7 +1186,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511->powered = false; adv7511->status = connector_status_disconnected; - ret = adv7511_parse_dt(dev->of_node, &link_config); + if (dev->of_node) { + const struct of_device_id *of_id; + of_id = of_match_node(adv7511_of_ids, dev->of_node); + adv7511->type = (unsigned long)of_id->data; + } else { + adv7511->type = id->driver_data; + } + + memset(&link_config, 0, sizeof(link_config)); + + ret = adv7511_parse_dt(adv7511, dev->of_node, &link_config); if (ret) return ret; @@ -897,10 +1222,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return ret; dev_dbg(dev, "Rev. %d\n", val); - ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers, + if (adv7511->type == ADV7511) { + ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers, ARRAY_SIZE(adv7511_fixed_registers)); - if (ret) - return ret; + if (ret) + return ret; + } else { + ret = regmap_register_patch(adv7511->regmap, adv7533_fixed_registers, + ARRAY_SIZE(adv7533_fixed_registers)); + if (ret) + return ret; + } regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, @@ -913,6 +1245,26 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511->i2c_edid) return -ENOMEM; + adv7511->i2c_cec = i2c_new_dummy(i2c->adapter, cec_i2c_addr >> 1); + if (!adv7511->i2c_cec) { + ret = -ENOMEM; + goto err_i2c_unregister_edid; + } + + adv7511->regmap_cec = devm_regmap_init_i2c(adv7511->i2c_cec, + &adv7533_cec_regmap_config); + if (IS_ERR(adv7511->regmap_cec)) { + ret = PTR_ERR(adv7511->regmap_cec); + goto err_i2c_unregister_cec; + } + + if (adv7511->type == ADV7533) { + ret = regmap_register_patch(adv7511->regmap_cec, adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); + if (ret) + return ret; + } + if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -921,7 +1273,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) IRQF_ONESHOT, dev_name(dev), adv7511); if (ret) - goto err_i2c_unregister_device; + goto err_i2c_unregister_cec; + + adv7511->irq = i2c->irq; } /* CEC is unused for now */ @@ -932,11 +1286,15 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) i2c_set_clientdata(i2c, adv7511); +// adv7511_audio_init(dev); + adv7511_set_link_config(adv7511, &link_config); return 0; -err_i2c_unregister_device: +err_i2c_unregister_cec: + i2c_unregister_device(adv7511->i2c_cec); +err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); return ret; @@ -946,6 +1304,8 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); +// adv7511_audio_exit(&i2c->dev); + i2c_unregister_device(adv7511->i2c_cec); i2c_unregister_device(adv7511->i2c_edid); kfree(adv7511->edid); @@ -953,6 +1313,15 @@ static int adv7511_remove(struct i2c_client *i2c) return 0; } +static const struct i2c_device_id adv7511_i2c_ids[] = { + { "adv7511", ADV7511 }, + { "adv7511w", ADV7511 }, + { "adv7513", ADV7511 }, + { "adv7533", ADV7533 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); + static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, struct drm_encoder_slave *encoder) { @@ -967,22 +1336,6 @@ static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, return 0; } -static const struct i2c_device_id adv7511_i2c_ids[] = { - { "adv7511", 0 }, - { "adv7511w", 0 }, - { "adv7513", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); - -static const struct of_device_id adv7511_of_ids[] = { - { .compatible = "adi,adv7511", }, - { .compatible = "adi,adv7511w", }, - { .compatible = "adi,adv7513", }, - { } -}; -MODULE_DEVICE_TABLE(of, adv7511_of_ids); - static struct drm_i2c_encoder_driver adv7511_driver = { .i2c_driver = { .driver = { @@ -1008,6 +1361,193 @@ static void __exit adv7511_exit(void) drm_i2c_encoder_unregister(&adv7511_driver); } module_exit(adv7511_exit); +#else + +static int adv7533_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct adv7511 *adv; + struct adv7511_link_config link_config; + struct i2c_adapter *adapter; + struct device_node *adapter_node; + unsigned int val; + int irq; + int ret; + + adv = devm_kzalloc(dev, sizeof(struct adv7511), GFP_KERNEL); + if (!adv) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, adv); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE + | MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; + + adv->type = ADV7533; + adv->powered = false; + adv->status = connector_status_disconnected; + + memset(&link_config, 0, sizeof(link_config)); + + ret = adv7511_parse_dt(adv, dev->of_node, &link_config); + if (ret) + return ret; + + adv->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); + if (IS_ERR(adv->gpio_pd)) + return PTR_ERR(adv->gpio_pd); + + if (adv->gpio_pd) { + mdelay(5); + gpiod_set_value_cansleep(adv->gpio_pd, 0); + } + + adapter_node = of_parse_phandle(dev->of_node, "i2c-bus", 0); + if (adapter_node) { + adapter = of_find_i2c_adapter_by_node(adapter_node); + if (adapter == NULL) + return -ENODEV; + } else { + return -ENODEV; + } + + adv->i2c_main = i2c_new_dummy(adapter, main_i2c_addr >> 1); + if (!adv->i2c_main) + return -ENOMEM; + + adv->regmap = devm_regmap_init_i2c(adv->i2c_main, &adv7511_regmap_config); + if (IS_ERR(adv->regmap)) + return PTR_ERR(adv->regmap); + + ret = regmap_read(adv->regmap, ADV7511_REG_CHIP_REVISION, &val); + if (ret) + return ret; + + dev_dbg(dev, "Rev. %d\n", val); + + ret = regmap_register_patch(adv->regmap, adv7533_fixed_registers, + ARRAY_SIZE(adv7533_fixed_registers)); + if (ret) + return ret; + + regmap_write(adv->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); + regmap_write(adv->regmap, ADV7511_REG_PACKET_I2C_ADDR, + packet_i2c_addr); + regmap_write(adv->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr); + adv7511_packet_disable(adv, 0xffff); + + adv->i2c_edid = i2c_new_dummy(adapter, edid_i2c_addr >> 1); + if (!adv->i2c_edid) { + ret = -ENOMEM; + goto err_i2c_unregister_main; + } + + adv->i2c_cec = i2c_new_dummy(adapter, cec_i2c_addr >> 1); + if (!adv->i2c_cec) { + ret = -ENOMEM; + goto err_i2c_unregister_edid; + } + + adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec, + &adv7533_cec_regmap_config); + if (IS_ERR(adv->regmap_cec)) { + ret = PTR_ERR(adv->regmap_cec); + goto err_i2c_unregister_cec; + } + + ret = regmap_register_patch(adv->regmap_cec, + adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); + if (ret) { + goto err_i2c_unregister_cec; + } + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (irq) { + init_waitqueue_head(&adv->wq); + + ret = devm_request_threaded_irq(dev, irq, NULL, + adv7511_irq_handler, + IRQF_ONESHOT, dev_name(dev), + adv); + if (ret) + goto err_i2c_unregister_cec; + + adv->irq = irq; + } + + /* CEC is unused for now */ + regmap_write(adv->regmap, ADV7511_REG_CEC_CTRL, + ADV7511_CEC_CTRL_POWER_DOWN); + + adv7511_power_off(adv); + +// adv7511_audio_init(dev); + + adv7511_set_link_config(adv, &link_config); + + adv->bridge.funcs = &adv7511_bridge_funcs; + adv->bridge.of_node = dev->of_node; + + ret = drm_bridge_add(&adv->bridge); + if (ret) { + DRM_ERROR("Failed to add adv7533 bridge\n"); + return ret; + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + goto err_bridge_remove; + + return 0; + +err_bridge_remove: + drm_bridge_remove(&adv->bridge); +err_i2c_unregister_cec: + i2c_unregister_device(adv->i2c_cec); +err_i2c_unregister_edid: + i2c_unregister_device(adv->i2c_edid); +err_i2c_unregister_main: + i2c_unregister_device(adv->i2c_main); + + return ret; +} + +static int adv7533_remove(struct mipi_dsi_device *dsi) +{ + struct adv7511 *adv = mipi_dsi_get_drvdata(dsi); + +// adv7511_audio_exit(&dsi->dev); + + i2c_unregister_device(adv->i2c_main); + i2c_unregister_device(adv->i2c_cec); + i2c_unregister_device(adv->i2c_edid); + + mipi_dsi_detach(dsi); + drm_bridge_remove(&adv->bridge); + kfree(adv->edid); + + return 0; +} + +static struct of_device_id adv7533_of_match[] = { + { .compatible = "adi,adv7533" }, + { } +}; +MODULE_DEVICE_TABLE(of, adv7533_of_match); + +static struct mipi_dsi_driver adv7533_driver = { + .probe = adv7533_probe, + .remove = adv7533_remove, + .driver = { + .name = "adv7533", + .of_match_table = adv7533_of_match, + }, +}; +module_mipi_dsi_driver(adv7533_driver); +#endif MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver"); diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h index 6599ed538426d6..26c9045f6b465a 100644 --- a/drivers/gpu/drm/i2c/adv7511.h +++ b/drivers/gpu/drm/i2c/adv7511.h @@ -10,6 +10,16 @@ #define __DRM_I2C_ADV7511_H__ #include +#include + +struct regmap; +struct adv7511; + +int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet); +int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet); + +int adv7511_audio_init(struct device *dev); +void adv7511_audio_exit(struct device *dev); #define ADV7511_REG_CHIP_REVISION 0x00 #define ADV7511_REG_N0 0x01 @@ -229,6 +239,54 @@ enum adv7511_sync_polarity { ADV7511_SYNC_POLARITY_HIGH, }; +enum adv7511_type { + ADV7511, + ADV7533, +}; + +struct adv7511 { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_cec; + + int irq; + + struct regmap *regmap; + struct regmap *regmap_cec; + enum drm_connector_status status; + bool powered; + + struct drm_display_mode *curr_mode; + + unsigned int f_tmds; + unsigned int f_audio; + unsigned int audio_source; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + struct drm_encoder *encoder; + + wait_queue_head_t wq; + +#ifndef CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER + struct drm_connector connector; + struct drm_bridge bridge; +#endif + + bool embedded_sync; + enum adv7511_sync_polarity vsync_polarity; + enum adv7511_sync_polarity hsync_polarity; + bool rgb; + u8 num_dsi_lanes; + + struct edid *edid; + + struct gpio_desc *gpio_pd; + + enum adv7511_type type; +}; + /** * struct adv7511_link_config - Describes adv7511 hardware configuration * @input_color_depth: Number of bits per color component (8, 10 or 12) @@ -241,6 +299,7 @@ enum adv7511_sync_polarity { * @sync_pulse: Select the sync pulse * @vsync_polarity: vsync input signal configuration * @hsync_polarity: hsync input signal configuration + * @num_dsi_lanes: number of dsi lanes in use(for ADV7533) */ struct adv7511_link_config { unsigned int input_color_depth; @@ -255,6 +314,8 @@ struct adv7511_link_config { enum adv7511_input_sync_pulse sync_pulse; enum adv7511_sync_polarity vsync_polarity; enum adv7511_sync_polarity hsync_polarity; + + unsigned int num_dsi_lanes; }; /** From 9612692b40be408dd65126ecfd667955d2e63135 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 11 Jun 2015 07:25:04 +0800 Subject: [PATCH 06/28] adv7511 hikey integrate Signed-off-by: Andy Green --- arch/arm64/boot/dts/hi6220.dtsi | 3 +++ drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index d6ad83606b48ed..205689f2902f79 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -677,6 +677,9 @@ pd-gpio = <&gpio0 4 0>; adi,input-depth = <8>; adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,clock-delay = <0>; + adi,embedded-sync; }; }; diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 6741cd09eefecb..fa0c42d7857a94 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -1014,10 +1014,15 @@ static int hisi_dsi_probe(struct platform_device *pdev) return -EPROBE_DEFER; } + if (!dsi->client->dev.driver) { + DRM_INFO("%s: NULL client driver\n", __func__); + return -EPROBE_DEFER; + } + dsi->drm_i2c_driver = to_drm_i2c_encoder_driver( to_i2c_driver(dsi->client->dev.driver)); - if (!dsi->drm_i2c_driver) { - DRM_INFO("failed initialize encoder driver\n"); + if (IS_ERR(dsi->drm_i2c_driver)) { + pr_err("failed initialize encoder driver %ld\n", PTR_ERR(dsi->drm_i2c_driver)); return -EPROBE_DEFER; } From 2e0424792fd1d2591251a882a12812524caa6379 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 11 Jun 2015 08:33:49 +0800 Subject: [PATCH 07/28] drm adv7511 make num lanes decideable dynamically The existing code seems to expect there is only one setting for the number of lanes in use. On a pluggable HDMI device, the number of lanes needed changes dynamically depending on the mode that is chosen. This patch makes the number of lanes optional in the dts, if left out then it's decided dynamically, on the heuristic that pixel clock > 80MHz == 4 lanes, otherwise 3 Signed-off-by: Andy Green --- drivers/gpu/drm/i2c/adv7511.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 74f8bd39ba2dd0..a825bd6fa68fe7 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -465,6 +465,7 @@ static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) regmap_write(adv7511->regmap_cec, 0x27, 0x8b); regmap_write(adv7511->regmap_cec, 0x27, 0xcb); #else + /* disable internal timing generator */ regmap_write(adv7511->regmap_cec, 0x27, 0x0b); #endif From 9d71e40a680d36314d7d9079a432e8474d2078a1 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 8 Jun 2015 15:30:18 +0800 Subject: [PATCH 08/28] drm hikey ade fixup real adjusted clock and disallow 72MHz This patch iteratively selects the ADE pixel clock, optionally in order to always choose the next highest clock, but always to avoid 72MHz base clock, which later selects 576MHz DSI clock which is unreliable, it only locks correctly about 50% of boots. Storing the actual chosen clock in adj_mode is used elsewhere to provide correct computations in later patches. Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_ade.c | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c index 98b0b128471aef..cb9fbe48acdbff 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c @@ -25,6 +25,7 @@ #include "hisi_ldi_reg.h" #include "hisi_drm_ade.h" +#define FORCE_PIXEL_CLOCK_SAME_OR_HIGHER 0 #define SC_MEDIA_RSTDIS (0x530) #define SC_MEDIA_RSTEN (0x52C) @@ -304,9 +305,34 @@ static void hisi_drm_crtc_dpms(struct drm_crtc *crtc, int mode) static bool hisi_drm_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *adj_mode) { + struct hisi_drm_ade_crtc *crtc_ade = to_hisi_crtc(crtc); + u32 clock_kHz = mode->clock; + int ret; + DRM_DEBUG_DRIVER("mode_fixup enter successfully.\n"); + + do { + ret = clk_set_rate(crtc_ade->ade_pix_clk, clock_kHz * 1000); + if (ret) { + DRM_ERROR("set ade_pixel_clk_rate fail\n"); + return false; + } + adj_mode->clock = clk_get_rate(crtc_ade->ade_pix_clk) / 1000; +#if FORCE_PIXEL_CLOCK_SAME_OR_HIGHER + if (adj_mode->clock >= clock_kHz) +#endif + /* This avoids a bad 720p DSI clock with 1.2GHz DPI PLL */ + if (adj_mode->clock != 72000) + break; + + clock_kHz += 10; + } while (1); + + pr_info("%s: pixel clock: req %dkHz -> actual: %dkHz\n", + __func__, mode->clock, adj_mode->clock); + DRM_DEBUG_DRIVER("mode_fixup exit successfully.\n"); return true; } From 5b2b811604e20915dbf67157852c2179d7effc0d Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 8 Jun 2015 15:37:48 +0800 Subject: [PATCH 09/28] drm hikey ade remove unhelpful helpers The helpers bloat the code and hide what's actually going in the registers. I'm a bit dubious about these bitfields always mapping correctly as well, also get rid of those (-94 LOC) Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_ade.c | 66 ++++++++--------- drivers/gpu/drm/hisilicon/hisi_ldi_reg.h | 94 ------------------------ 2 files changed, 33 insertions(+), 127 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c index cb9fbe48acdbff..404d20ff0f57e5 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c @@ -225,54 +225,54 @@ static int hisi_drm_crtc_ade_disable(struct hisi_drm_ade_crtc *crtc_ade) static void ldi_init(struct hisi_drm_ade_crtc *crtc_ade) { - int ret; - u32 hfront_porch, hback_porch, hsync_len; - u32 vfront_porch, vback_porch, vsync_len; + struct drm_display_mode *mode = crtc_ade->dmode; + void __iomem *ade_base = crtc_ade->ade_base; + u32 hfp, hbp, hsw, vfp, vbp, vsw; u32 plr_flags; u32 ldi_mask; - struct drm_display_mode *mode = crtc_ade->dmode; - u8 __iomem *ade_base = crtc_ade->ade_base; - /* - * Timing setting - */ plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? HISI_LDI_FLAG_NVSYNC : 0; plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? HISI_LDI_FLAG_NHSYNC : 0; - hfront_porch = mode->hsync_start - mode->hdisplay; - hback_porch = mode->htotal - mode->hsync_end; - hsync_len = mode->hsync_end - mode->hsync_start; - vfront_porch = mode->vsync_start - mode->vdisplay; - vback_porch = mode->vtotal - mode->vsync_end; - vsync_len = mode->vsync_end - mode->vsync_start; - if (vsync_len > 15) - vsync_len = 15; - - set_LDI_HRZ_CTRL0(ade_base, hfront_porch, hback_porch); - set_LDI_HRZ_CTRL1_hsw(ade_base, hsync_len); - set_LDI_VRT_CTRL0(ade_base, vfront_porch, vback_porch); - set_LDI_VRT_CTRL1_vsw(ade_base, vsync_len); - writel(plr_flags, ade_base + LDI_PLR_CTRL_REG); - set_LDI_DSP_SIZE_size(ade_base, mode->hdisplay, mode->vdisplay); - ret = clk_set_rate(crtc_ade->ade_pix_clk, mode->clock * 1000); - if (ret) { - DRM_ERROR("set ade_pixel_clk_rate fail\n"); - return; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + hsw = mode->hsync_end - mode->hsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + vsw = mode->vsync_end - mode->vsync_start; + if (vsw > 15) { + pr_err("%s: vsw exceeded 15\n", __func__); + vsw = 15; } + writel((hbp << 20) | (hfp << 0), ade_base + LDI_HRZ_CTRL0_REG); + /* p3-73 6220V100 pdf: + * "The configured value is the actual width - 1" + */ + writel(hsw - 1, ade_base + LDI_HRZ_CTRL1_REG); + writel((vbp << 20) | (vfp << 0), ade_base + LDI_VRT_CTRL0_REG); + /* p3-74 6220V100 pdf: + * "The configured value is the actual width - 1" + */ + writel(vsw - 1, ade_base + LDI_VRT_CTRL1_REG); + + /* p3-75 6220V100 pdf: + * "The configured value is the actual width - 1" + */ + writel(((mode->vdisplay - 1) << 20) | ((mode->hdisplay - 1) << 0), + ade_base + LDI_DSP_SIZE_REG); + writel(plr_flags, ade_base + LDI_PLR_CTRL_REG); + /* * other parameters setting */ - set_LDI_WORK_MODE_work_mode(ade_base, LDI_WORK); - set_LDI_WORK_MODE_colorbar_en(ade_base, ADE_DISABLE); + writel(BIT(0), ade_base + LDI_WORK_MODE_REG); ldi_mask = LDI_ISR_FRAME_END_INT | LDI_ISR_UNDER_FLOW_INT; writel(ldi_mask, ade_base + LDI_INT_EN_REG); + writel((0x3c << 6) | (ADE_OUT_RGB_888 << 3) | BIT(2) | BIT(0), + ade_base + LDI_CTRL_REG); - set_LDI_CTRL_bgr(ade_base, ADE_RGB); - set_LDI_CTRL_bpp(ade_base, ADE_OUT_RGB_888); - set_LDI_CTRL_disp_mode(ade_base, LDI_DISP_MODE_NOT_3D_FBF); - set_LDI_CTRL_corlorbar_width(ade_base, 0x3C); writel(0xFFFFFFFF, ade_base + LDI_INT_CLR_REG); set_reg(ade_base + LDI_DE_SPACE_LOW_REG, 0x1, 1, 1); /* dsi pixel on */ diff --git a/drivers/gpu/drm/hisilicon/hisi_ldi_reg.h b/drivers/gpu/drm/hisilicon/hisi_ldi_reg.h index e59f2dec717b13..f89ced7cedc8ff 100644 --- a/drivers/gpu/drm/hisilicon/hisi_ldi_reg.h +++ b/drivers/gpu/drm/hisilicon/hisi_ldi_reg.h @@ -40,48 +40,6 @@ #define HISI_LDI_FLAG_NDE BIT(3) /********** LDI Register Union Struct ***********/ -union U_LDI_HRZ_CTRL0 { -struct { - unsigned int hfp :12; - unsigned int Reserved_564 :8; - unsigned int hbp :12; - } bits; - unsigned int u32; -}; - -union U_LDI_HRZ_CTRL1 { -struct { - unsigned int hsw :12; - unsigned int Reserved_566 :20; - } bits; - unsigned int u32; -}; - -union U_LDI_VRT_CTRL0 { -struct { - unsigned int vfp :12; - unsigned int Reserved_567 :8; - unsigned int vbp :12; - } bits; - unsigned int u32; -}; - -union U_LDI_VRT_CTRL1 { -struct { - unsigned int vsw :12; - unsigned int Reserved_568 :20; - } bits; - unsigned int u32; -}; - -union U_LDI_DSP_SIZE { -struct { - unsigned int hsize :12; - unsigned int Reserved_570 :8; - unsigned int vsize :12; - } bits; - unsigned int u32; -}; union U_LDI_CTRL { struct { @@ -122,58 +80,6 @@ static inline void set_reg(u8 *addr, u32 val, u32 bw, u32 bs) writel(tmp | ((val & mask) << bs), addr); } -static inline void set_LDI_HRZ_CTRL0(u8 *ade_base, u32 hfp, u32 hbp) -{ - volatile union U_LDI_HRZ_CTRL0 ldi_hrz_ctrl0; - u8 *addr = ade_base + LDI_HRZ_CTRL0_REG; - - ldi_hrz_ctrl0.u32 = readl(addr); - ldi_hrz_ctrl0.bits.hfp = hfp; - ldi_hrz_ctrl0.bits.hbp = hbp; - writel(ldi_hrz_ctrl0.u32, addr); -} - -static inline void set_LDI_HRZ_CTRL1_hsw(u8 *ade_base, u32 nVal) -{ - volatile union U_LDI_HRZ_CTRL1 ldi_hrz_ctrl1; - u8 *addr = ade_base + LDI_HRZ_CTRL1_REG; - - ldi_hrz_ctrl1.u32 = readl(addr); - ldi_hrz_ctrl1.bits.hsw = (nVal > 0) ? nVal - 1 : 0; - writel(ldi_hrz_ctrl1.u32, addr); -} - -static inline void set_LDI_VRT_CTRL0(u8 *ade_base, u32 vfp, u32 vbp) -{ - volatile union U_LDI_VRT_CTRL0 ldi_vrt_ctrl0; - u8 *addr = ade_base + LDI_VRT_CTRL0_REG; - - ldi_vrt_ctrl0.u32 = readl(addr); - ldi_vrt_ctrl0.bits.vfp = vfp; - ldi_vrt_ctrl0.bits.vbp = vbp; - writel(ldi_vrt_ctrl0.u32, addr); -} - -static inline void set_LDI_VRT_CTRL1_vsw(u8 *ade_base, u32 nVal) -{ - volatile union U_LDI_VRT_CTRL1 ldi_vrt_ctrl1; - u8 *addr = ade_base + LDI_VRT_CTRL1_REG; - - ldi_vrt_ctrl1.u32 = readl(addr); - ldi_vrt_ctrl1.bits.vsw = (nVal > 0) ? nVal - 1 : 0; - writel(ldi_vrt_ctrl1.u32, addr); -} - -static inline void set_LDI_DSP_SIZE_size(u8 *ade_base, u32 hVal, u32 vVal) -{ - volatile union U_LDI_DSP_SIZE ldi_dsp_size; - u8 *addr = ade_base + LDI_DSP_SIZE_REG; - - ldi_dsp_size.bits.hsize = (hVal > 0) ? hVal-1 : 0; - ldi_dsp_size.bits.vsize = (vVal > 0) ? vVal-1 : 0; - writel(ldi_dsp_size.u32, addr); -} - static inline void set_LDI_CTRL_ldi_en(u8 *ade_base, u32 nVal) { volatile union U_LDI_CTRL ldi_ctrl; From ed860f22146b34298d438fabd44a471f52098877 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 3 Jun 2015 11:52:20 +0800 Subject: [PATCH 10/28] drm hisi remove forced modes Framebuffer console no longer needs this to work, EDID fetch and 3-lane restriction automatically selects 720p or best mode available on monitor. Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 39 ------------------------ 1 file changed, 39 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index fa0c42d7857a94..1d7708a02ca885 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -41,11 +41,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; - struct mipi_dsi_phy_register { u32 clk_t_lpx; u32 clk_t_hs_prepare; @@ -830,38 +827,6 @@ 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; - - DRM_DEBUG_DRIVER("enter.\n"); - mode = drm_mode_create(connector->dev); - if (!mode) { - DRM_ERROR("failed to create a new display mode\n"); - return 0; - } - - mode->vrefresh = 60; - mode->clock = 75000; - mode->hdisplay = 1280; - mode->hsync_start = 1500; - mode->hsync_end = 1540; - mode->htotal = 1650; - mode->vdisplay = 720; - mode->vsync_start = 740; - mode->vsync_end = 745; - mode->vtotal = 750; - mode->type = 0x40; - mode->flags = 0xa; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - - DRM_DEBUG_DRIVER("exit successfully.\n"); - return 1; -} -#endif - static int hisi_dsi_get_modes(struct drm_connector *connector) { struct hisi_dsi *dsi __maybe_unused = connector_to_dsi(connector); @@ -871,12 +836,8 @@ static int hisi_dsi_get_modes(struct drm_connector *connector) int count = 0; DRM_DEBUG_DRIVER("enter.\n"); -#if USE_DEFAULT_720P_MODE - count = hisi_get_default_modes(connector); -#else if (sfuncs->get_modes) count = sfuncs->get_modes(encoder, connector); -#endif DRM_DEBUG_DRIVER("exit success. count=%d\n", count); return count; } From d1227e1b773c6abd9d2ed5c2cf8cbf0c7fefb360 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 8 Jun 2015 15:55:07 +0800 Subject: [PATCH 11/28] drm hisi use kHz for clock computations 1) Enhance all the computations to use ps period resolution and kHz frequency resolution, improving accuracy. 2) Automatically use 4 lanes when clock rate > 80MHz, for compatibility with 1080p 3) I found enable and disable of the DSI LP mode when other transmissions can occur was necessary to make each mode work. By trial and error I found what is needed for each mode and implemented heuristics. 4) Some modes require one more byte lane clock compared to the calculated amount, I added a heuristic to do it With these changes 1080p50 and 60 modes work on all 3 of my monitors, and 720p60 can also work. 720p50 worked on one monitor but on another he was flicking between showing it correctly and feeling the mode was invalid, so I disabled it. There is a colour swap problem on 1080p60 I noticed sometimes. On some boots what should be R, G, B are misordered as B, R, G, I think it means we slipped a colour byte sent by DSI: RGBRGBRGBRGB received by ADV7533: RGBRGBRGBRGB This happens between DSI and ADV7533 because - it's the first place the pixels are serialized - swapping monitors after this happens it never changes afterwards (ie, problem is not happening in the monitor receiver) I only just noticed it so not sure what to do about it yet. Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 509 ++++++++++++----------- 1 file changed, 272 insertions(+), 237 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 1d7708a02ca885..369252dbbeadd0 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -41,6 +41,8 @@ #define DSI_BURST_MODE DSI_NON_BURST_SYNC_PULSES #define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define DEFAULT_MIPI_CLK_PERIOD_PS (1000000000 / (DEFAULT_MIPI_CLK_RATE / 1000)) + u8 *reg_base_mipi_dsi; struct mipi_dsi_phy_register { @@ -74,7 +76,7 @@ struct mipi_dsi_phy_register { u32 phy_hs2lp_time; u32 clk_to_data_delay; u32 data_to_clk_delay; - u32 lane_byte_clk; + u32 lane_byte_clk_kHz; u32 clk_division; u32 burst_mode; }; @@ -92,7 +94,7 @@ struct hisi_dsi { u32 lanes; u32 format; - struct mipi_dsi_phy_register phy_register; + struct mipi_dsi_phy_register phyreg; u32 date_enable_pol; u32 vc; u32 mode_flags; @@ -112,8 +114,8 @@ enum { }; struct dsi_phy_seq_info { - u32 min_range; - u32 max_range; + u32 min_range_kHz; + u32 max_range_kHz; u32 rg_pll_vco_750M; u32 rg_hstx_ckg_sel; }; @@ -126,16 +128,16 @@ enum { }; struct dsi_phy_seq_info dphy_seq_info[] = { - {46, 62, 1, 7}, - {62, 93, 0, 7}, - {93, 125, 1, 6}, - {125, 187, 0, 6}, - {187, 250, 1, 5}, - {250, 375, 0, 5}, - {375, 500, 1, 4}, - {500, 750, 0, 4}, - {750, 1000, 1, 0}, - {1000, 1500, 0, 0} + { 46000, 62000, 1, 7 }, + { 62000, 93000, 0, 7 }, + { 93000, 125000, 1, 6 }, + { 125000, 187000, 0, 6 }, + { 187000, 250000, 1, 5 }, + { 250000, 375000, 0, 5 }, + { 375000, 500000, 1, 4 }, + { 500000, 750000, 0, 4 }, + { 750000, 1000000, 1, 0 }, + { 1000000, 1500000, 0, 0 } }; static inline void set_reg(u8 *addr, u32 val, u32 bw, u32 bs) @@ -153,168 +155,168 @@ static inline struct drm_encoder_slave_funcs * return to_encoder_slave(enc)->slave_funcs; } -void get_dsi_phy_register(u32 *phy_freq, - struct mipi_dsi_phy_register *phy_register) +void set_dsi_phy_rate_equal_or_faster(u32 *phy_freq_kHz, + struct mipi_dsi_phy_register *phyreg) { u32 ui = 0; - u32 t_cfg_clk = 0; - u32 seq_info_count = 0; + u32 cfg_clk_ps = DEFAULT_MIPI_CLK_PERIOD_PS; u32 i = 0; - u32 q_pll = 0; + u32 q_pll = 1; u32 m_pll = 0; u32 n_pll = 0; - u32 r_pll = 0; + u32 r_pll = 1; u32 m_n = 0; u32 m_n_int = 0; + u64 f_kHz; + u64 temp; - DRM_DEBUG_DRIVER("enter.\n"); - BUG_ON(phy_freq == NULL); - BUG_ON(phy_register == NULL); - - t_cfg_clk = 1000 / (DEFAULT_MIPI_CLK_RATE / 1000000); - - /* PLL parameters calculation */ - seq_info_count = sizeof(dphy_seq_info) / sizeof(struct dsi_phy_seq_info); - for (i = 0; i < seq_info_count; i++) { - if (*phy_freq > dphy_seq_info[i].min_range - && *phy_freq <= dphy_seq_info[i].max_range) { - phy_register->rg_pll_vco_750M = dphy_seq_info[i].rg_pll_vco_750M; - phy_register->rg_hstx_ckg_sel = dphy_seq_info[i].rg_hstx_ckg_sel; - break; - } - } + DRM_DEBUG_DRIVER("enter (phy_freq_kHz = %u)\n", *phy_freq_kHz); + BUG_ON(phyreg == NULL); - switch (phy_register->rg_hstx_ckg_sel) { - case 7: - q_pll = 16; - break; - case 6: - q_pll = 8; - break; - case 5: - q_pll = 4; - break; - case 4: - q_pll = 2; - break; - default: - q_pll = 1; - break; - } + do { + f_kHz = *phy_freq_kHz; - m_n_int = (*phy_freq) * q_pll * t_cfg_clk / 1000; - m_n = (*phy_freq) * q_pll * t_cfg_clk % 1000 * 10 / 1000; - if (m_n_int % 2 == 0) { - if (m_n * 6 >= 50) { - n_pll = 2; - m_pll = (m_n_int + 1) * n_pll; - } else if (m_n * 6 >= 30) { - n_pll = 3; - m_pll = m_n_int * n_pll + 2; - } else { - n_pll = 1; - m_pll = m_n_int * n_pll; + /* Find the PLL clock range from the table */ + + for (i = 0; i < ARRAY_SIZE(dphy_seq_info); i++) + if (f_kHz > dphy_seq_info[i].min_range_kHz && + f_kHz <= dphy_seq_info[i].max_range_kHz) + break; + + if (i == ARRAY_SIZE(dphy_seq_info)) { + pr_err("%s: %lldkHz out of range\n", __func__, f_kHz); + return; } - } else { - if (m_n * 6 >= 50) { - n_pll = 1; - m_pll = (m_n_int + 1) * n_pll; - } else if (m_n * 6 >= 30) { - n_pll = 1; - m_pll = (m_n_int + 1) * n_pll; - } else if (m_n * 6 >= 10) { - n_pll = 3; - m_pll = m_n_int * n_pll + 1; + + phyreg->rg_pll_vco_750M = dphy_seq_info[i].rg_pll_vco_750M; + phyreg->rg_hstx_ckg_sel = dphy_seq_info[i].rg_hstx_ckg_sel; + + if (phyreg->rg_hstx_ckg_sel <= 7 && + phyreg->rg_hstx_ckg_sel >= 4) + q_pll = 0x10 >> (7 - phyreg->rg_hstx_ckg_sel); + + temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps; + m_n_int = temp / (u64)1000000000; + m_n = (temp % (u64)1000000000) / (u64)100000000; + + pr_debug("%s: m_n_int = %d, m_n = %d\n", + __func__, m_n_int, m_n); + + if (m_n_int % 2 == 0) { + if (m_n * 6 >= 50) { + n_pll = 2; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 3; + m_pll = m_n_int * n_pll + 2; + } else { + n_pll = 1; + m_pll = m_n_int * n_pll; + } } else { - n_pll = 2; - m_pll = m_n_int * n_pll; + if (m_n * 6 >= 50) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 10) { + n_pll = 3; + m_pll = m_n_int * n_pll + 1; + } else { + n_pll = 2; + m_pll = m_n_int * n_pll; + } } - } - - if (n_pll == 1) { - phy_register->rg_pll_fbd_p = 0; - phy_register->rg_pll_pre_div1p = 1; - } else { - phy_register->rg_pll_fbd_p = n_pll; - phy_register->rg_pll_pre_div1p = 0; - } - switch (phy_register->rg_pll_fbd_2p) { - case 7: - r_pll = 16; - break; - case 6: - r_pll = 8; - break; - case 5: - r_pll = 4; - break; - case 4: - r_pll = 2; - break; - default: - r_pll = 1; - break; - } + if (n_pll == 1) { + phyreg->rg_pll_fbd_p = 0; + phyreg->rg_pll_pre_div1p = 1; + } else { + phyreg->rg_pll_fbd_p = n_pll; + phyreg->rg_pll_pre_div1p = 0; + } - if (m_pll == 2) { - phy_register->rg_pll_pre_p = 0; - phy_register->rg_pll_fbd_s = 0; - phy_register->rg_pll_fbd_div1f = 0; - phy_register->rg_pll_fbd_div5f = 1; - } else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) { - phy_register->rg_pll_pre_p = m_pll / (2 * r_pll); - phy_register->rg_pll_fbd_s = 0; - phy_register->rg_pll_fbd_div1f = 1; - phy_register->rg_pll_fbd_div5f = 0; - } else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) { - if (((m_pll / (2 * r_pll)) % 2) == 0) { - phy_register->rg_pll_pre_p = (m_pll / (2 * r_pll)) / 2 - 1; - phy_register->rg_pll_fbd_s = (m_pll / (2 * r_pll)) % 2 + 2; + if (phyreg->rg_pll_fbd_2p <= 7 && phyreg->rg_pll_fbd_2p >= 4) + r_pll = 0x10 >> (7 - phyreg->rg_pll_fbd_2p); + + if (m_pll == 2) { + phyreg->rg_pll_pre_p = 0; + phyreg->rg_pll_fbd_s = 0; + phyreg->rg_pll_fbd_div1f = 0; + phyreg->rg_pll_fbd_div5f = 1; + } else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) { + phyreg->rg_pll_pre_p = m_pll / (2 * r_pll); + phyreg->rg_pll_fbd_s = 0; + phyreg->rg_pll_fbd_div1f = 1; + phyreg->rg_pll_fbd_div5f = 0; + } else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) { + if (((m_pll / (2 * r_pll)) % 2) == 0) { + phyreg->rg_pll_pre_p = + (m_pll / (2 * r_pll)) / 2 - 1; + phyreg->rg_pll_fbd_s = + (m_pll / (2 * r_pll)) % 2 + 2; + } else { + phyreg->rg_pll_pre_p = + (m_pll / (2 * r_pll)) / 2; + phyreg->rg_pll_fbd_s = + (m_pll / (2 * r_pll)) % 2; + } + phyreg->rg_pll_fbd_div1f = 0; + phyreg->rg_pll_fbd_div5f = 0; } else { - phy_register->rg_pll_pre_p = (m_pll / (2 * r_pll)) / 2; - phy_register->rg_pll_fbd_s = (m_pll / (2 * r_pll)) % 2; + phyreg->rg_pll_pre_p = 0; + phyreg->rg_pll_fbd_s = 0; + phyreg->rg_pll_fbd_div1f = 0; + phyreg->rg_pll_fbd_div5f = 1; } - phy_register->rg_pll_fbd_div1f = 0; - phy_register->rg_pll_fbd_div5f = 0; - } else { - phy_register->rg_pll_pre_p = 0; - phy_register->rg_pll_fbd_s = 0; - phy_register->rg_pll_fbd_div1f = 0; - phy_register->rg_pll_fbd_div5f = 1; - } - *phy_freq = 1000 * m_pll / (t_cfg_clk * n_pll * q_pll); - ui = 1000 / (*phy_freq); - - phy_register->clk_t_lpx = ROUND(50, 8 * ui); - phy_register->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1; - - phy_register->clk_t_hs_zero = ROUND(262, 8 * ui); - phy_register->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1); - phy_register->clk_t_wakeup = ROUND(1000000, t_cfg_clk - 1) > 0xFF ? 0xFF : ROUND(1000000, t_cfg_clk - 1); - phy_register->data_t_wakeup = phy_register->clk_t_wakeup; - phy_register->data_t_lpx = phy_register->clk_t_lpx; - phy_register->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1; - phy_register->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui); - phy_register->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1); - phy_register->data_t_ta_go = 3; - phy_register->data_t_ta_get = 4; - - phy_register->rg_pll_enbwt = 1; - phy_register->phy_clklp2hs_time = ROUND(407, 8 * ui) + 12; - phy_register->phy_clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui); - phy_register->phy_lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1; - phy_register->phy_hs2lp_time = phy_register->phy_clkhs2lp_time; - phy_register->clk_to_data_delay = 1 + phy_register->phy_clklp2hs_time; - phy_register->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) + phy_register->phy_clkhs2lp_time; - - phy_register->lane_byte_clk = *phy_freq / 8; - phy_register->clk_division = ((phy_register->lane_byte_clk % MAX_TX_ESC_CLK) > 0) ? - (phy_register->lane_byte_clk / MAX_TX_ESC_CLK + 1) : - (phy_register->lane_byte_clk / MAX_TX_ESC_CLK); - - phy_register->burst_mode = DSI_BURST_MODE; + f_kHz = (u64)1000000000 * (u64)m_pll / + ((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll); + + if (f_kHz >= *phy_freq_kHz) + break; + + (*phy_freq_kHz) += 10; + + } while (1); + + pr_info("%s: %dkHz -> %lldkHz\n", __func__, *phy_freq_kHz, f_kHz); + + *phy_freq_kHz = f_kHz; + ui = 1000000 / f_kHz; + + phyreg->clk_t_lpx = ROUND(50, 8 * ui); + phyreg->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1; + + phyreg->clk_t_hs_zero = ROUND(262, 8 * ui); + phyreg->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1); + phyreg->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1); + if (phyreg->clk_t_wakeup > 0xff) + phyreg->clk_t_wakeup = 0xff; + phyreg->data_t_wakeup = phyreg->clk_t_wakeup; + phyreg->data_t_lpx = phyreg->clk_t_lpx; + phyreg->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1; + phyreg->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui); + phyreg->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1); + phyreg->data_t_ta_go = 3; + phyreg->data_t_ta_get = 4; + + phyreg->rg_pll_enbwt = 1; + phyreg->phy_clklp2hs_time = ROUND(407, 8 * ui) + 12; + phyreg->phy_clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui); + phyreg->phy_lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1; + phyreg->phy_hs2lp_time = phyreg->phy_clkhs2lp_time; + phyreg->clk_to_data_delay = 1 + phyreg->phy_clklp2hs_time; + phyreg->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) + + phyreg->phy_clkhs2lp_time; + + phyreg->lane_byte_clk_kHz = f_kHz / 8; + phyreg->clk_division = phyreg->lane_byte_clk_kHz / MAX_TX_ESC_CLK; + if (phyreg->lane_byte_clk_kHz % MAX_TX_ESC_CLK) + phyreg->clk_division++; + + phyreg->burst_mode = DSI_BURST_MODE; DRM_DEBUG_DRIVER("exit success.\n"); } @@ -324,18 +326,21 @@ int mipi_init(struct hisi_dsi *dsi) u32 hline_time = 0; u32 hsa_time = 0; u32 hbp_time = 0; - u32 pixel_clk = 0; + u32 pixel_clk_kHz; u32 i = 0; bool is_ready = false; u32 delay_count = 0; - struct mipi_dsi_phy_register *phy_register = &dsi->phy_register; + struct mipi_dsi_phy_register *phyreg = &dsi->phyreg; + int refresh; + + pr_info("%s: lanes %d\n", __func__, dsi->lanes); DRM_DEBUG_DRIVER("enter.\n"); /* reset Core */ set_MIPIDSI_PWR_UP_shutdownz(0); set_MIPIDSI_PHY_IF_CFG_n_lanes(dsi->lanes-1); - set_MIPIDSI_CLKMGR_CFG_tx_esc_clk_division(phy_register->clk_division); + set_MIPIDSI_CLKMGR_CFG_tx_esc_clk_division(phyreg->clk_division); set_MIPIDSI_PHY_RSTZ(0x00000000); set_MIPIDSI_PHY_TST_CTRL0(0x00000000); @@ -346,7 +351,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10010); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->clk_t_lpx); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->clk_t_lpx); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -354,7 +359,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10011); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->clk_t_hs_prepare); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->clk_t_hs_prepare); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -362,7 +367,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10012); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->clk_t_hs_zero); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->clk_t_hs_zero); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -370,7 +375,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10013); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->clk_t_hs_trial); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->clk_t_hs_trial); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -378,7 +383,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10014); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->clk_t_wakeup); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->clk_t_wakeup); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -388,7 +393,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10020 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_lpx); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_lpx); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -396,7 +401,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10021 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_hs_prepare); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_hs_prepare); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -404,7 +409,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10022 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_hs_zero); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_hs_zero); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -412,7 +417,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10023 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_hs_trial); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_hs_trial); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -420,7 +425,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10024 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_ta_go); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_ta_go); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -428,7 +433,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10025 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_ta_get); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_ta_get); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -436,7 +441,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10026 + (i << 4)); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->data_t_wakeup); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->data_t_wakeup); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); } @@ -445,7 +450,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10060); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->rg_hstx_ckg_sel); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->rg_hstx_ckg_sel); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -453,9 +458,9 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10063); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1((phy_register->rg_pll_fbd_div5f << 5) + - (phy_register->rg_pll_fbd_div1f << 4) + (phy_register->rg_pll_fbd_2p << 1) + - phy_register->rg_pll_enbwt); + set_MIPIDSI_PHY_TST_CTRL1((phyreg->rg_pll_fbd_div5f << 5) + + (phyreg->rg_pll_fbd_div1f << 4) + (phyreg->rg_pll_fbd_2p << 1) + + phyreg->rg_pll_enbwt); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -463,7 +468,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10064); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1(phy_register->rg_pll_fbd_p); + set_MIPIDSI_PHY_TST_CTRL1(phyreg->rg_pll_fbd_p); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -471,7 +476,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10065); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1((1 << 4) + phy_register->rg_pll_fbd_s); + set_MIPIDSI_PHY_TST_CTRL1((1 << 4) + phyreg->rg_pll_fbd_s); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -479,8 +484,8 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10066); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1((phy_register->rg_pll_pre_div1p << 7) + - phy_register->rg_pll_pre_p); + set_MIPIDSI_PHY_TST_CTRL1((phyreg->rg_pll_pre_div1p << 7) + + phyreg->rg_pll_pre_p); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -488,8 +493,8 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_PHY_TST_CTRL1(0x10067); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); - set_MIPIDSI_PHY_TST_CTRL1((5 << 5) + (phy_register->rg_pll_vco_750M << 4) + - (phy_register->rg_pll_lpf_rs << 2) + phy_register->rg_pll_lpf_cs); + set_MIPIDSI_PHY_TST_CTRL1((5 << 5) + (phyreg->rg_pll_vco_750M << 4) + + (phyreg->rg_pll_lpf_rs << 2) + phyreg->rg_pll_lpf_cs); set_MIPIDSI_PHY_TST_CTRL0(0x2); set_MIPIDSI_PHY_TST_CTRL0(0x0); @@ -539,16 +544,17 @@ int mipi_init(struct hisi_dsi *dsi) * 2. Configure the DPI Interface: * This defines how the DPI interface interacts with the controller. * - * 3. Configure the TX_ESC clock frequency to a frequency lower than 20 MHz + * 3. Configure the TX_ESC clock frequency to < 20 MHz * that is the maximum allowed frequency for D-PHY ESCAPE mode. - * - */ + */ set_MIPIDSI_DPI_VCID(dsi->vc); set_MIPIDSI_DPI_COLOR_CODING_dpi_color_coding(dsi->color_mode); - set_MIPIDSI_DPI_CFG_POL_hsync_active_low(dsi->vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ? 0 : 1); + set_MIPIDSI_DPI_CFG_POL_hsync_active_low( + dsi->vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ? 0 : 1); set_MIPIDSI_DPI_CFG_POL_dataen_active_low(dsi->date_enable_pol); - set_MIPIDSI_DPI_CFG_POL_vsync_active_low(dsi->vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ? 0 : 1); + set_MIPIDSI_DPI_CFG_POL_vsync_active_low( + dsi->vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ? 0 : 1); set_MIPIDSI_DPI_CFG_POL_shutd_active_low(0); set_MIPIDSI_DPI_CFG_POL_colorm_active_low(0); if (dsi->format == MIPI_DSI_FMT_RGB666) @@ -557,27 +563,47 @@ int mipi_init(struct hisi_dsi *dsi) /* * 4. Define the DPI Horizontal timing configuration: * - * Hsa_time = HSA*(PCLK period/Clk Lane Byte Period); - * Hbp_time = HBP*(PCLK period/Clk Lane Byte Period); + * Hsa_time = HSA * (PCLK period / Clk Lane Byte Period); + * Hbp_time = HBP * (PCLK period / Clk Lane Byte Period); * Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period); - */ + * + * NOTICE that below, we adjust the DSI transmitter view of the + * horizontal timings to approximate the timings we gave the + * CRTC in pixel clocks, in terms of DSI byte lane clocks. + * + * Eg, 1080p60 CRTC/DRM hsa is 44 pixel clocks, but maybe 33 + * DSI byte lane clocks + */ + + pixel_clk_kHz = dsi->vm.pixelclock; + refresh = (pixel_clk_kHz * 1000) / (( + dsi->vm.hactive + dsi->vm.hsync_len + + dsi->vm.hfront_porch + dsi->vm.hback_porch) * ( + dsi->vm.vactive + dsi->vm.vsync_len + + dsi->vm.vfront_porch + dsi->vm.vback_porch)); + + /* the actual clock may be significantly (3%) slower */ + if (refresh >= 48 && refresh < 50) + refresh = 50; + + hsa_time = (dsi->vm.hsync_len * phyreg->lane_byte_clk_kHz) / + pixel_clk_kHz; + hbp_time = (dsi->vm.hback_porch * phyreg->lane_byte_clk_kHz) / + pixel_clk_kHz; + hline_time = ((dsi->vm.hsync_len + dsi->vm.hback_porch + + dsi->vm.hactive + dsi->vm.hfront_porch) * + phyreg->lane_byte_clk_kHz) / pixel_clk_kHz; - 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 + - dsi->vm.hactive + dsi->vm.hfront_porch) * - phy_register->lane_byte_clk / pixel_clk; set_MIPIDSI_VID_HSA_TIME(hsa_time); set_MIPIDSI_VID_HBP_TIME(hbp_time); set_MIPIDSI_VID_HLINE_TIME(hline_time); - DRM_INFO("%s,pixcel_clk=%d,lane_byte_clk=%d,hsa=%d,hbp=%d,hline=%d", __func__, - pixel_clk, phy_register->lane_byte_clk, hsa_time, hbp_time, hline_time); + DRM_INFO("%s: pixel_clk_kHz=%d, lane_byte_clk_kHz=%d, hsa=%d, " + "hbp=%d, hline=%d", __func__, pixel_clk_kHz, + phyreg->lane_byte_clk_kHz, hsa_time, hbp_time, hline_time); /* * 5. Define the Vertical line configuration: - * - */ + */ if (dsi->vm.vsync_len > 15) dsi->vm.vsync_len = 15; @@ -587,39 +613,47 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_VID_VACTIVE_LINES(dsi->vm.vactive); set_MIPIDSI_VID_PKT_SIZE(dsi->vm.hactive); +/* + * Good results entirely depended which of these LP modes were + * enabled per timing mode. + */ + pr_info("%s: timing mode %dx%d@%d\n", __func__, dsi->vm.hactive, + dsi->vm.vactive, refresh); + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { - set_MIPIDSI_VID_MODE_CFG_lp_vsa_en(1); - set_MIPIDSI_VID_MODE_CFG_lp_vbp_en(1); - set_MIPIDSI_VID_MODE_CFG_lp_vfp_en(1); - set_MIPIDSI_VID_MODE_CFG_lp_vact_en(1); - set_MIPIDSI_VID_MODE_CFG_lp_hbp_en(1); - set_MIPIDSI_VID_MODE_CFG_lp_hfp_en(1); - /*VSA/VBP/VFP max transfer byte in LP mode*/ + set_MIPIDSI_VID_MODE_CFG_lp_vsa_en(0); + set_MIPIDSI_VID_MODE_CFG_lp_vbp_en(0); + set_MIPIDSI_VID_MODE_CFG_lp_vfp_en(0); + set_MIPIDSI_VID_MODE_CFG_lp_vact_en(0); + set_MIPIDSI_VID_MODE_CFG_lp_hbp_en(0); + set_MIPIDSI_VID_MODE_CFG_lp_hfp_en(0); + + /*VSA/VBP/VFP max transfer byte in LP mode*/ set_MIPIDSI_DPI_CFG_LP_TIM(0); - /*enable LP command transfer*/ - set_MIPIDSI_VID_MODE_CFG_lp_cmd_en(1); - /*config max read time*/ - set_MIPIDSI_PHY_TMR_CFG_max_rd_time(0xFFFF); + /* enable LP command transfer */ + set_MIPIDSI_VID_MODE_CFG_lp_cmd_en(0); + /* config max read time */ + set_MIPIDSI_PHY_TMR_CFG_max_rd_time(0xFF); } /* Configure core's phy parameters */ set_MIPIDSI_BTA_TO_CNT_bta_to_cnt(4095); - set_MIPIDSI_PHY_TMR_CFG_phy_lp2hs_time(phy_register->phy_lp2hs_time); - set_MIPIDSI_PHY_TMR_CFG_phy_hs2lp_time(phy_register->phy_hs2lp_time); - set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clklp2hs_time(phy_register->phy_clklp2hs_time); - set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clkhs2lp_time(phy_register->phy_clkhs2lp_time); - set_MIPIDSI_PHY_TMR_clk_to_data_delay(phy_register->clk_to_data_delay); - set_MIPIDSI_PHY_TMR_data_to_clk_delay(phy_register->data_to_clk_delay); + set_MIPIDSI_PHY_TMR_CFG_phy_lp2hs_time(phyreg->phy_lp2hs_time); + set_MIPIDSI_PHY_TMR_CFG_phy_hs2lp_time(phyreg->phy_hs2lp_time); + set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clklp2hs_time(phyreg->phy_clklp2hs_time); + set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clkhs2lp_time(phyreg->phy_clkhs2lp_time); + set_MIPIDSI_PHY_TMR_clk_to_data_delay(phyreg->clk_to_data_delay); + set_MIPIDSI_PHY_TMR_data_to_clk_delay(phyreg->data_to_clk_delay); /* * 3. Select the Video Transmission Mode: * This defines how the processor requires the video line to be * transported through the DSI link. */ set_MIPIDSI_VID_MODE_CFG_frame_bta_ack_en(0); - set_MIPIDSI_VID_MODE_CFG_vid_mode_type(phy_register->burst_mode); + set_MIPIDSI_VID_MODE_CFG_vid_mode_type(phyreg->burst_mode); set_MIPIDSI_LPCLK_CTRL_auto_clklane_ctrl(0); /* for dsi read */ - set_MIPIDSI_PCKHDL_CFG_bta_en(1); + set_MIPIDSI_PCKHDL_CFG_bta_en(0); /* Enable EOTP TX; Enable EDPI, ALLOWED_CMD_SIZE = 720*/ if (dsi->mode_flags == MIPI_DSI_MODE_VIDEO) set_MIPIDSI_EDPI_CMD_SIZE(dsi->vm.hactive); @@ -733,11 +767,11 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, struct hisi_dsi *dsi = encoder_to_dsi(encoder); struct videomode *vm = &dsi->vm; struct drm_encoder_slave_funcs *sfuncs = get_slave_funcs(encoder); - u32 dphy_freq_need; - u32 dphy_freq_true; + u32 dphy_freq_kHz; DRM_DEBUG_DRIVER("enter.\n"); - vm->pixelclock = mode->clock/1000; + vm->pixelclock = adjusted_mode->clock; + vm->hactive = mode->hdisplay; vm->vactive = mode->vdisplay; vm->vfront_porch = mode->vsync_start - mode->vdisplay; @@ -747,12 +781,15 @@ 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 */ - if (vm->vactive == 720 && vm->pixelclock == 75) - dphy_freq_true = dphy_freq_need = 640; /* for 720p 640M is more stable */ - else - dphy_freq_true = dphy_freq_need = vm->pixelclock*24/dsi->lanes; - get_dsi_phy_register(&dphy_freq_true, &dsi->phy_register); + dsi->lanes = 3 + !!(vm->pixelclock >= 115000); + mipi_init(dsi); + + /* laneBitRate >= pixelClk * bpp /lanes */ + dphy_freq_kHz = vm->pixelclock /* mode->clock */ * 24 / dsi->lanes; +// if (dphy_freq_kHz == 576000) +// dphy_freq_kHz = 640000; + + set_dsi_phy_rate_equal_or_faster(&dphy_freq_kHz, &dsi->phyreg); vm->flags = 0; if (mode->flags & DRM_MODE_FLAG_PHSYNC) @@ -766,8 +803,8 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, if (sfuncs->mode_set) sfuncs->mode_set(encoder, mode, adjusted_mode); - DRM_DEBUG_DRIVER("exit success: pixelclk=%d,dphy_freq_need=%d, dphy_freq_true=%d\n", - (u32)vm->pixelclock, dphy_freq_need, dphy_freq_true); + DRM_DEBUG_DRIVER("exit success: pixelclk=%dkHz, dphy_freq_kHz=%dkHz\n", + (u32)vm->pixelclock, dphy_freq_kHz); } static void hisi_drm_encoder_prepare(struct drm_encoder *encoder) @@ -861,13 +898,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; - /* For 3 lanes bandwith is limited */ - if (mode->vdisplay > 1000) - return MODE_BAD_VVALUE; - - DRM_DEBUG_DRIVER("enter.\n"); - if (sfuncs->mode_valid) + if (sfuncs->mode_valid) { ret = sfuncs->mode_valid(encoder, mode); + if (ret != MODE_OK) + return ret; + } DRM_DEBUG_DRIVER("exit success. ret=%d\n", ret); return ret; From 46dfe97239e1340f17ab65c4b66c37d188dd58e7 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 17 Jun 2015 18:23:57 +0800 Subject: [PATCH 12/28] V3 drm hisi provide canned 720p if no other mode This creates a canned DRM mode for 720p60, and in the event there is no usable mode left after DRM checks validity, the canned mode will be used. This is helpful in two cases 1) No EDID appeared, but the device supports the fallback mode 2) An EDID appeared, but it does not list any acceptable mode. However, the device does actually support the fallback mode. It's implemented by adding a new optional connector func callback which is called in the event we are left with no valid modes. Connectors may handle the "fallback_mode" callback and add a fallback mode to use rather than the DRM default of 1024 x 768. In the case no EDID came but you forced your mode with video= on the kernel commandline, normally DRM will attempt to synthesize appropriate mode timings. But these very seldom work with HDMI monitors. In the case a mode was forced that does not exist in the EDID already but a fallback mode exists, this patch makes it choose the fallback mode and ignore the mode forcing. You can test "no EDID" by shorting HDMI connector p16 (SDA) to p17 (0V) Signed-off-by: Andy Green --- drivers/gpu/drm/drm_fb_helper.c | 10 ++++++ drivers/gpu/drm/drm_probe_helper.c | 21 ++++++++---- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 43 +++++++++++++++++++++++- include/drm/drm_crtc.h | 2 ++ 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c0c39bac23da2..767adfcb8a3771 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1262,6 +1262,16 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f } create_mode: + /* + * Synthetic mode timing is very unlikely to work on HDMI. Instead + * if there is a fallback mode, give up on trying to guess the forced + * mode timing and just let the forced mode deal with it. + */ + if (fb_helper_conn->connector->funcs->fallback_mode) { + pr_err("%s: preferring fallback mode\n", __func__); + return NULL; + } + mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, cmdline_mode); list_add(&mode->head, &fb_helper_conn->connector->modes); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6857e9ad6339c7..f240a689e92fa7 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -155,11 +155,15 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect count = (*connector_funcs->get_modes)(connector); } - if (count == 0 && connector->status == connector_status_connected) + if (count == 0 && connector->status == connector_status_connected && + !connector->funcs->fallback_mode) count = drm_add_modes_noedid(connector, 1024, 768); - count += drm_helper_probe_add_cmdline_mode(connector); - if (count == 0) - goto prune; + + if (count || !connector->funcs->fallback_mode) { + count += drm_helper_probe_add_cmdline_mode(connector); + if (count == 0) + goto prune; + } drm_mode_connector_list_update(connector, merge_type_bits); @@ -183,8 +187,13 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect prune: drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); - if (list_empty(&connector->modes)) - return 0; + if (list_empty(&connector->modes)) { + if (!connector->funcs->fallback_mode) + return 0; + if (connector->funcs->fallback_mode(connector)) + return 0; + count = 1; + } list_for_each_entry(mode, &connector->modes, head) mode->vrefresh = drm_mode_vrefresh(mode); diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 369252dbbeadd0..fcc1edb3ffd4d9 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -140,6 +140,34 @@ struct dsi_phy_seq_info dphy_seq_info[] = { { 1000000, 1500000, 0, 0 } }; +/* + * Canned 720p60 mode for use if no whitelisted modes + * available (due to no EDID or EDID contains no whitelisted + * mode) + * + * Detailed mode: Clock 74.250 MHz, 735 mm x 420 mm + * 1280 1390 1430 1650 hborder 0 + * 720 725 730 750 vborder 0 + * +hsync +vsync + */ + +static struct drm_display_mode mode_720p_canned = { + .name = "720p60", + .type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER, + .clock = 74250, + .hdisplay = 1280, + .hsync_start = 1390, + .hsync_end = 1430, + .htotal = 1650, + .vdisplay = 720, + .vsync_start = 725, + .vsync_end = 730, + .vtotal = 750, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, + .width_mm = 735, + .height_mm = 420, +}; + static inline void set_reg(u8 *addr, u32 val, u32 bw, u32 bs) { u32 mask = (1 << bw) - 1; @@ -857,11 +885,24 @@ static void hisi_dsi_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } +static int hisi_dsi_fallback_mode(struct drm_connector *connector) +{ + struct drm_display_mode *mode = kmalloc(sizeof(*mode), GFP_KERNEL); + + pr_info("%s: adding canned fallback 720p mode\n", __func__); + memcpy(mode, &mode_720p_canned, sizeof(*mode)); + + list_add_tail(&mode->head, &connector->modes); + + return 0; +} + static struct drm_connector_funcs hisi_dsi_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = hisi_dsi_detect, - .destroy = hisi_dsi_connector_destroy + .destroy = hisi_dsi_connector_destroy, + .fallback_mode = hisi_dsi_fallback_mode, }; static int hisi_dsi_get_modes(struct drm_connector *connector) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c40070a92d6b43..2b47aa1c5def2e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -411,6 +411,8 @@ struct drm_connector_funcs { uint64_t val); void (*destroy)(struct drm_connector *connector); void (*force)(struct drm_connector *connector); + /* optional mode connector wants to fall back to instead of 1024x768 */ + int (*fallback_mode)(struct drm_connector *connector); }; /** From 68c50d4fc547b562add48d7776bda9bd7ee18a54 Mon Sep 17 00:00:00 2001 From: Xinliang Liu Date: Thu, 11 Jun 2015 19:50:02 +0800 Subject: [PATCH 13/28] 720p dsi clock 640MHz Signed-off-by: Xinliang Liu --- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index fcc1edb3ffd4d9..08dc9e50fc830a 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -814,8 +814,8 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, /* laneBitRate >= pixelClk * bpp /lanes */ dphy_freq_kHz = vm->pixelclock /* mode->clock */ * 24 / dsi->lanes; -// if (dphy_freq_kHz == 576000) -// dphy_freq_kHz = 640000; + if (dphy_freq_kHz == 600000) + dphy_freq_kHz = 640000; set_dsi_phy_rate_equal_or_faster(&dphy_freq_kHz, &dsi->phyreg); From 46135d519e93a1d1ca449d07650388401a2d1543 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 14 Jun 2015 09:22:52 +0800 Subject: [PATCH 14/28] drm hisi dsi compute and log true timings When the pixel clock differs from the DSI byte lane clock, the timing for h blanking related items becomes fractional. So we can understand the relationship, log the affected timings to three decimal places. Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 72 +++++++++++++++++++++--- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 08dc9e50fc830a..4b6baed8732684 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -88,6 +88,7 @@ struct hisi_dsi { struct drm_i2c_encoder_driver *drm_i2c_driver; struct clk *dsi_cfg_clk; struct videomode vm; + int nominal_pixel_clock_kHz; u8 __iomem *reg_base; u8 color_mode; @@ -359,7 +360,8 @@ int mipi_init(struct hisi_dsi *dsi) bool is_ready = false; u32 delay_count = 0; struct mipi_dsi_phy_register *phyreg = &dsi->phyreg; - int refresh; + int refresh, refresh_nom, refresh_real, htot, vtot, blc_hactive; + int tmp, tmp1; pr_info("%s: lanes %d\n", __func__, dsi->lanes); @@ -603,12 +605,13 @@ int mipi_init(struct hisi_dsi *dsi) * DSI byte lane clocks */ + htot = dsi->vm.hactive + dsi->vm.hsync_len + + dsi->vm.hfront_porch + dsi->vm.hback_porch; + vtot = dsi->vm.vactive + dsi->vm.vsync_len + + dsi->vm.vfront_porch + dsi->vm.vback_porch; + pixel_clk_kHz = dsi->vm.pixelclock; - refresh = (pixel_clk_kHz * 1000) / (( - dsi->vm.hactive + dsi->vm.hsync_len + - dsi->vm.hfront_porch + dsi->vm.hback_porch) * ( - dsi->vm.vactive + dsi->vm.vsync_len + - dsi->vm.vfront_porch + dsi->vm.vback_porch)); + refresh = (pixel_clk_kHz * 1000) / (htot * vtot); /* the actual clock may be significantly (3%) slower */ if (refresh >= 48 && refresh < 50) @@ -618,9 +621,11 @@ int mipi_init(struct hisi_dsi *dsi) pixel_clk_kHz; hbp_time = (dsi->vm.hback_porch * phyreg->lane_byte_clk_kHz) / pixel_clk_kHz; - hline_time = ((dsi->vm.hsync_len + dsi->vm.hback_porch + - dsi->vm.hactive + dsi->vm.hfront_porch) * - phyreg->lane_byte_clk_kHz) / pixel_clk_kHz; + hline_time = (((u64)htot * (u64)phyreg->lane_byte_clk_kHz)) / + pixel_clk_kHz; + blc_hactive = (((u64)dsi->vm.hactive * (u64)phyreg->lane_byte_clk_kHz)) / + pixel_clk_kHz; + set_MIPIDSI_VID_HSA_TIME(hsa_time); set_MIPIDSI_VID_HBP_TIME(hbp_time); @@ -641,6 +646,54 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_VID_VACTIVE_LINES(dsi->vm.vactive); set_MIPIDSI_VID_PKT_SIZE(dsi->vm.hactive); + refresh_nom = ((u64)dsi->nominal_pixel_clock_kHz * 1000000) / + (htot * vtot); + + tmp = 1000000000 / dsi->vm.pixelclock; + tmp1 = 1000000000 / phyreg->lane_byte_clk_kHz; + + pr_info("%s: Pixel clock: %ldkHz (%d.%03dns), " + "DSI bytelane clock: %dkHz (%d.%03dns)\n", + __func__, dsi->vm.pixelclock, tmp / 1000, tmp % 1000, + phyreg->lane_byte_clk_kHz, tmp1 / 1000, tmp1 % 1000); + + pr_info("%s: CLK HACT VACT REFRSH HTOT VTOT HFP HSA HBP VFP VBP VSA\n", + __func__); + +/* returns pixel clocks in dsi byte lane times, multiplied by 1000 */ +#define R(x) ((u32)((((u64)(x) * (u64)1000 * (u64)dsi->vm.pixelclock) / \ + phyreg->lane_byte_clk_kHz))) + + refresh_real = ((u64)dsi->vm.pixelclock * (u64)1000000000) / + ((u64)R(hline_time) * (u64)vtot); + + pr_info("%s: nom: %6u %4u %4u %2u.%03u %4u %4u %3u %3u %3u %3u %3u %3u\n", + __func__, dsi->nominal_pixel_clock_kHz, + dsi->vm.hactive, dsi->vm.vactive, + refresh_nom / 1000, refresh_nom % 1000, htot, vtot, + dsi->vm.hfront_porch, dsi->vm.hsync_len, + dsi->vm.hback_porch, dsi->vm.vfront_porch, + dsi->vm.vsync_len, dsi->vm.vback_porch); + + pr_info("%s: tru: %6u %4u %4u %2u.%03u %4u.%03u %4u %3u.%03u %3u.%03u %3u.%03u %3u %3u %3u\n", + __func__, (u32)dsi->vm.pixelclock, + dsi->vm.hactive, dsi->vm.vactive, + refresh_real / 1000, refresh_real % 1000, + R(hline_time) / 1000, R(hline_time) % 1000, vtot, + R(hline_time - hbp_time - hsa_time - blc_hactive) / 1000, + R(hline_time - hbp_time - hsa_time - blc_hactive) % 1000, + R(hsa_time) / 1000, R(hsa_time) % 1000, + R(hbp_time) / 1000, R(hbp_time) % 1000, + dsi->vm.vfront_porch, + dsi->vm.vsync_len, dsi->vm.vback_porch); + +/* +Andy's patch 1280 720 1655 750 115 40 222 5 20 5 +before patch 1280 720 1760 750 260 42 180 20 5 5 +standard 1280 720 1650 750 110 40 220 5 20 5 +*/ + + /* * Good results entirely depended which of these LP modes were * enabled per timing mode. @@ -799,6 +852,7 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, DRM_DEBUG_DRIVER("enter.\n"); vm->pixelclock = adjusted_mode->clock; + dsi->nominal_pixel_clock_kHz = mode->clock; vm->hactive = mode->hdisplay; vm->vactive = mode->vdisplay; From 8dd339ad7277d9e673a8033875faa175ad7f5a7d Mon Sep 17 00:00:00 2001 From: Xinwei Kong Date: Wed, 17 Jun 2015 20:30:04 +0800 Subject: [PATCH 15/28] drm hisi ade avoid setting powered-down DPI --- drivers/gpu/drm/hisilicon/hisi_drm_ade.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c index 404d20ff0f57e5..b339f13f10ec85 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c @@ -109,6 +109,7 @@ struct hisi_drm_ade_crtc { struct clk *ade_core_clk; struct clk *media_noc_clk; struct clk *ade_pix_clk; + bool power_on; }; @@ -176,6 +177,7 @@ static int ade_power_up(struct hisi_drm_ade_crtc *crtc_ade) DRM_ERROR("fail to clk_prepare_enable ade_core_clk\n"); return ret; } + crtc_ade->power_on = true; return 0; } @@ -186,6 +188,7 @@ static int ade_power_down(struct hisi_drm_ade_crtc *crtc_ade) clk_disable_unprepare(crtc_ade->ade_core_clk); writel(0x20, media_base + SC_MEDIA_RSTEN); clk_disable_unprepare(crtc_ade->media_noc_clk); + crtc_ade->power_on = false; return 0; } @@ -193,10 +196,12 @@ static int hisi_drm_crtc_ade_enable(struct hisi_drm_ade_crtc *crtc_ade) { int ret; - ret = ade_power_up(crtc_ade); - if (ret) { + if (!crtc_ade->power_on) { + ret = ade_power_up(crtc_ade); + if (ret) { DRM_ERROR("failed to initialize ade clk\n"); return ret; + } } ade_init(crtc_ade); @@ -313,6 +318,10 @@ static bool hisi_drm_crtc_mode_fixup(struct drm_crtc *crtc, DRM_DEBUG_DRIVER("mode_fixup enter successfully.\n"); + if (!crtc_ade->power_on) + if (ade_power_up(crtc_ade)) + DRM_ERROR("%s: failed to power up ade\n", __func__); + do { ret = clk_set_rate(crtc_ade->ade_pix_clk, clock_kHz * 1000); if (ret) { @@ -492,6 +501,7 @@ static int hisi_drm_crtc_create(struct hisi_drm_ade_crtc *crtc_ade) int ret; crtc_ade->enable = false; + crtc_ade->power_on = false; ret = drm_crtc_init(crtc_ade->drm_dev, crtc, &crtc_funcs); if (ret < 0) return ret; From 632320394b70c655161af9543f15c9e23976e0c8 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 18 Jun 2015 11:04:04 +0800 Subject: [PATCH 16/28] drm hisi ade use correct stride to allow scanout and fb size to differ The existing code here assumes that the framebuffer size is always the scanout size. There is no requirement that is true, and eg, if you start on a 1080p mode and change mode in the framebuffer console to 720p later, the framebuffer console remains at 1080p and the new mode is a subwindow on the 1080p sized original framebuffer console. This patch corrects the computations to use the right mix of information about the memory layout (framebuffer stride) and the current scanout (crtc active mode dimensions) so it works for all cases. Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_ade.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c index b339f13f10ec85..22e5b66ef7bfca 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_ade.c @@ -386,6 +386,7 @@ static int hisi_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, u32 display_addr; u32 offset; u32 fb_hight; + int bytes_pp = (fb->bits_per_pixel + 1) / 8; ade_base = crtc_ade->ade_base; stride = fb->pitches[0]; @@ -394,9 +395,11 @@ static int hisi_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, fb_hight = hisi_fb->is_fbdev_fb ? fb->height / HISI_NUM_FRAMEBUFFERS : fb->height; - DRM_DEBUG_DRIVER("enter stride=%d,paddr=0x%x,display_addr=0x%x,%dx%d\n", - stride, (u32)obj->paddr, display_addr, - fb->width, fb_hight); + DRM_DEBUG_DRIVER("enter: fb stride=%d, paddr=0x%x, display_addr=0x%x, " + "fb=%dx%d, scanout=%dx%d\n", + stride, (u32)obj->paddr, display_addr, + fb->width, fb_hight, crtc->mode.hdisplay, + crtc->mode.vdisplay); /* TOP setting */ writel(0, ade_base + ADE_WDMA2_SRC_CFG_REG); @@ -432,19 +435,20 @@ static int hisi_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, #endif /* CONFIG_ANDROID */ writel(display_addr, ade_base + RD_CH_DISP_ADDR_REG); - writel((fb_hight << 16) | stride, ade_base + RD_CH_DISP_SIZE_REG); + writel((crtc->mode.vdisplay << 16) | crtc->mode.hdisplay * bytes_pp, + ade_base + RD_CH_DISP_SIZE_REG); writel(stride, ade_base + RD_CH_DISP_STRIDE_REG); - writel(fb_hight * stride, ade_base + RD_CH_DISP_SPACE_REG); + writel(crtc->mode.vdisplay * stride, ade_base + RD_CH_DISP_SPACE_REG); writel(1, ade_base + RD_CH_DISP_EN_REG); /* ctran5 setting */ writel(1, ade_base + ADE_CTRAN5_DIS_REG); - writel(fb->width * fb_hight - 1, + writel(crtc->mode.hdisplay * crtc->mode.vdisplay - 1, ade_base + ADE_CTRAN5_IMAGE_SIZE_REG); /* ctran6 setting */ writel(1, ade_base + ADE_CTRAN6_DIS_REG); - writel(fb->width * fb_hight - 1, + writel(crtc->mode.hdisplay * crtc->mode.vdisplay - 1, ade_base + ADE_CTRAN6_IMAGE_SIZE_REG); /* enable ade and ldi */ From 4310d6fd7943f6ab1779494e64ab12d50192579a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 18 Jun 2015 08:40:52 +0800 Subject: [PATCH 17/28] drm sysrq g mode hack This patch let you force cycling between the remaining DRM modes from the kernel alone. It can only deal with one CRTC. It's useful if a working display is needed for using or setting up a device, but the HDMI compatibility is not 100%. At least the user can check every valid mode to see if it can work on his display. Force the mode change by typing Alt-Gr SysRq g that is - hold down the right-hand ALT key - hold down the SysRq key - tap g key - let go of everything Each time he will move to the "next" mode, when at the last mode he will go back to the first. Signed-off-by: Andy Green --- drivers/gpu/drm/Kconfig | 11 +++++ drivers/gpu/drm/drm_crtc_helper.c | 74 +++++++++++++++++++++++++++++++ drivers/tty/sysrq.c | 14 ++++++ include/linux/sysrq.h | 3 ++ 4 files changed, 102 insertions(+) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3d7a045536a267..e61d99b7409b2b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -80,6 +80,17 @@ source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/bridge/Kconfig" +config DRM_SYSRQ_MODE_HACK + bool "Allow SysRq-g to cycle through modes (1 crtc only)" + depends on DRM + help + Select this option if your mode / timing generation is not so + reliable that you can always show the preferred mode on any + monitor and there are no arrangements on the platform to change + it by hand. You can use AltGr-Sysrq-g to cycle between the + available modes when this is enabled. It only understands one + CRTC. + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 6c65a0a28fbde3..fba41a5013b44c 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -221,6 +221,77 @@ drm_crtc_prepare_encoders(struct drm_device *dev) } } +#ifdef CONFIG_DRM_SYSRQ_MODE_HACK +static struct drm_mode_set modehack_set; +static void modehack_handler(struct work_struct *work) +{ + struct drm_connector *connector = NULL, *connector1; + struct drm_display_mode *mode1, *mode_first = NULL, *mode2 = NULL; + bool next = false; + + if (!modehack_set.crtc) + return; + + drm_modeset_lock_all(modehack_set.crtc->dev); + + /* find our connector */ + list_for_each_entry(connector1, + &modehack_set.crtc->dev->mode_config.connector_list, + head) { + if (!connector) + connector = connector1; + } + if (!connector) { + pr_err("no connector\n"); + goto bail; + } + + /* identify our current mode and the "next" */ + list_for_each_entry(mode1, &connector->modes, head) { + if (!mode_first) + mode_first = mode1; + if (next) { + next = false; + mode2 = mode1; + } + next = drm_mode_equal(&modehack_set.crtc->mode, mode1); + } + + /* if next never appeared, loop back to first one */ + if (!mode2) + mode2 = mode_first; + + pr_err("Trying %dx%d@%d, %dkHz\n", mode2->hdisplay, mode2->vdisplay, + ((mode2->clock * 1000) / (mode2->htotal * mode2->vtotal)), + mode2->clock); + + modehack_set.mode = mode2; + modehack_set.connectors = &connector; + modehack_set.num_connectors = 1; + + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_STANDBY); + drm_crtc_helper_set_config(&modehack_set); + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + +bail: + drm_modeset_unlock_all(modehack_set.crtc->dev); +} + +static DECLARE_WORK(modehack_work, modehack_handler); + +/* + * Alt-Gr SYSRQ g magically calls this + */ +void sysrq_handle_modehack(int key) +{ + if (!modehack_set.crtc) + return; + + schedule_work(&modehack_work); +} +EXPORT_SYMBOL_GPL(sysrq_handle_modehack); +#endif + /** * drm_crtc_helper_set_mode - internal helper to set a mode * @crtc: CRTC to program @@ -268,6 +339,9 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, return false; } + modehack_set.fb = crtc->primary->fb; + modehack_set.crtc = crtc; + saved_mode = crtc->mode; saved_x = crtc->x; saved_y = crtc->y; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 42bad18c66c938..2d56dcda1b69ed 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -408,6 +408,16 @@ static struct sysrq_key_op sysrq_unrt_op = { .enable_mask = SYSRQ_ENABLE_RTNICE, }; +#ifdef CONFIG_DRM_SYSRQ_MODE_HACK +extern void sysrq_handle_modehack(int key); +static struct sysrq_key_op sysrq_modehack_op = { + .handler = sysrq_handle_modehack, + .help_msg = "Change drm mode", + .action_msg = "Hack to switch DRM mode", + .enable_mask = SYSRQ_ENABLE_HACK_DRM_MODE, +}; +#endif + /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); @@ -434,7 +444,11 @@ static struct sysrq_key_op *sysrq_key_table[36] = { &sysrq_term_op, /* e */ &sysrq_moom_op, /* f */ /* g: May be registered for the kernel debugger */ +#ifdef CONFIG_DRM_SYSRQ_MODE_HACK + &sysrq_modehack_op, /* g */ +#else NULL, /* g */ +#endif NULL, /* h - reserved for help */ &sysrq_kill_op, /* i */ #ifdef CONFIG_BLOCK diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h index 387fa7d05c982b..177d92d4910bfb 100644 --- a/include/linux/sysrq.h +++ b/include/linux/sysrq.h @@ -27,6 +27,9 @@ #define SYSRQ_ENABLE_SIGNAL 0x0040 #define SYSRQ_ENABLE_BOOT 0x0080 #define SYSRQ_ENABLE_RTNICE 0x0100 +#ifdef CONFIG_DRM_SYSRQ_MODE_HACK +#define SYSRQ_ENABLE_HACK_DRM_MODE 0x0200 +#endif struct sysrq_key_op { void (*handler)(int); From 1ef4c91d0f4c0295a45daa7890e9b3a67430645a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 22 Jun 2015 08:04:29 +0800 Subject: [PATCH 18/28] drm hisi use sysrq g modehack For now having the sysrq-g mode hack is useful with hikey Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig index d4d42c9bffd792..35362ef89f460e 100644 --- a/drivers/gpu/drm/hisilicon/Kconfig +++ b/drivers/gpu/drm/hisilicon/Kconfig @@ -5,6 +5,7 @@ config DRM_HISI select DRM_GEM_CMA_HELPER select DRM_PANEL select DRM_MIPI_DSI + select DRM_SYSRQ_MODE_HACK help Choose this option if you have a hisilicon terminal chipset. If M is selected the module will be called hisi-drm. From 7ba4b45a35942279917a2ec3683b4ce158f3cdb1 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 21 Jun 2015 08:02:40 +0800 Subject: [PATCH 19/28] drm hisi dsi move init after clock calc Signed-off-by: Andy Green --- drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 109 ++++++++--------------- 1 file changed, 38 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c index 4b6baed8732684..a44bc39dfd1316 100644 --- a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c @@ -360,16 +360,15 @@ int mipi_init(struct hisi_dsi *dsi) bool is_ready = false; u32 delay_count = 0; struct mipi_dsi_phy_register *phyreg = &dsi->phyreg; - int refresh, refresh_nom, refresh_real, htot, vtot, blc_hactive; + int refresh_nom, refresh_real, htot, vtot, blc_hactive; int tmp, tmp1; pr_info("%s: lanes %d\n", __func__, dsi->lanes); - DRM_DEBUG_DRIVER("enter.\n"); /* reset Core */ set_MIPIDSI_PWR_UP_shutdownz(0); - set_MIPIDSI_PHY_IF_CFG_n_lanes(dsi->lanes-1); + set_MIPIDSI_PHY_IF_CFG_n_lanes(dsi->lanes - 1); set_MIPIDSI_CLKMGR_CFG_tx_esc_clk_division(phyreg->clk_division); set_MIPIDSI_PHY_RSTZ(0x00000000); @@ -565,18 +564,6 @@ int mipi_init(struct hisi_dsi *dsi) if (!is_ready) DRM_INFO("phystopstateclklane is not ready.\n"); - /* --------------configuring the DPI packet transmission---------------- - * - * 1. Global configuration - * Configure Register PHY_IF_CFG with the correct number of lanes - * to be used by the controller. - * - * 2. Configure the DPI Interface: - * This defines how the DPI interface interacts with the controller. - * - * 3. Configure the TX_ESC clock frequency to < 20 MHz - * that is the maximum allowed frequency for D-PHY ESCAPE mode. - */ set_MIPIDSI_DPI_VCID(dsi->vc); set_MIPIDSI_DPI_COLOR_CODING_dpi_color_coding(dsi->color_mode); @@ -591,18 +578,11 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_DPI_COLOR_CODING_loosely18_en(1); /* - * 4. Define the DPI Horizontal timing configuration: - * - * Hsa_time = HSA * (PCLK period / Clk Lane Byte Period); - * Hbp_time = HBP * (PCLK period / Clk Lane Byte Period); - * Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period); - * - * NOTICE that below, we adjust the DSI transmitter view of the - * horizontal timings to approximate the timings we gave the - * CRTC in pixel clocks, in terms of DSI byte lane clocks. - * - * Eg, 1080p60 CRTC/DRM hsa is 44 pixel clocks, but maybe 33 - * DSI byte lane clocks + * The DSI IP accepts vertical timing using lines as normal, + * but horizontal timing is a mixture of pixel-clocks for the + * active region and byte-lane clocks for the blanking-related + * timings. hfp is specified as the total hline_time in byte- + * lane clocks minus hsa, hbp and active. */ htot = dsi->vm.hactive + dsi->vm.hsync_len + @@ -611,11 +591,6 @@ int mipi_init(struct hisi_dsi *dsi) dsi->vm.vfront_porch + dsi->vm.vback_porch; pixel_clk_kHz = dsi->vm.pixelclock; - refresh = (pixel_clk_kHz * 1000) / (htot * vtot); - - /* the actual clock may be significantly (3%) slower */ - if (refresh >= 48 && refresh < 50) - refresh = 50; hsa_time = (dsi->vm.hsync_len * phyreg->lane_byte_clk_kHz) / pixel_clk_kHz; @@ -623,10 +598,20 @@ int mipi_init(struct hisi_dsi *dsi) pixel_clk_kHz; hline_time = (((u64)htot * (u64)phyreg->lane_byte_clk_kHz)) / pixel_clk_kHz; - blc_hactive = (((u64)dsi->vm.hactive * (u64)phyreg->lane_byte_clk_kHz)) / - pixel_clk_kHz; + blc_hactive = (((u64)dsi->vm.hactive * + (u64)phyreg->lane_byte_clk_kHz)) / pixel_clk_kHz; +/* returns pixel clocks in dsi byte lane times, multiplied by 1000 */ +#define R(x) ((u32)((((u64)(x) * (u64)1000 * (u64)dsi->vm.pixelclock) / \ + phyreg->lane_byte_clk_kHz))) + if ((R(hline_time) / 1000) > htot) + hline_time--; + + if ((R(hline_time) / 1000) < htot) + hline_time++; + + /* all specified in byte-lane clocks */ set_MIPIDSI_VID_HSA_TIME(hsa_time); set_MIPIDSI_VID_HBP_TIME(hbp_time); set_MIPIDSI_VID_HLINE_TIME(hline_time); @@ -634,9 +619,7 @@ int mipi_init(struct hisi_dsi *dsi) DRM_INFO("%s: pixel_clk_kHz=%d, lane_byte_clk_kHz=%d, hsa=%d, " "hbp=%d, hline=%d", __func__, pixel_clk_kHz, phyreg->lane_byte_clk_kHz, hsa_time, hbp_time, hline_time); - /* - * 5. Define the Vertical line configuration: - */ + if (dsi->vm.vsync_len > 15) dsi->vm.vsync_len = 15; @@ -644,7 +627,7 @@ int mipi_init(struct hisi_dsi *dsi) set_MIPIDSI_VID_VBP_LINES(dsi->vm.vback_porch); set_MIPIDSI_VID_VFP_LINES(dsi->vm.vfront_porch); set_MIPIDSI_VID_VACTIVE_LINES(dsi->vm.vactive); - set_MIPIDSI_VID_PKT_SIZE(dsi->vm.hactive); + set_MIPIDSI_VID_PKT_SIZE(dsi->vm.hactive); /* in DPI pixel clocks */ refresh_nom = ((u64)dsi->nominal_pixel_clock_kHz * 1000000) / (htot * vtot); @@ -687,21 +670,11 @@ int mipi_init(struct hisi_dsi *dsi) dsi->vm.vfront_porch, dsi->vm.vsync_len, dsi->vm.vback_porch); -/* -Andy's patch 1280 720 1655 750 115 40 222 5 20 5 -before patch 1280 720 1760 750 260 42 180 20 5 5 -standard 1280 720 1650 750 110 40 220 5 20 5 -*/ - - -/* - * Good results entirely depended which of these LP modes were - * enabled per timing mode. - */ - pr_info("%s: timing mode %dx%d@%d\n", __func__, dsi->vm.hactive, - dsi->vm.vactive, refresh); - if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { + /* + * we disable this since it affects downstream + * DSI -> HDMI converter output + */ set_MIPIDSI_VID_MODE_CFG_lp_vsa_en(0); set_MIPIDSI_VID_MODE_CFG_lp_vbp_en(0); set_MIPIDSI_VID_MODE_CFG_lp_vfp_en(0); @@ -721,33 +694,30 @@ standard 1280 720 1650 750 110 40 220 5 20 5 set_MIPIDSI_BTA_TO_CNT_bta_to_cnt(4095); set_MIPIDSI_PHY_TMR_CFG_phy_lp2hs_time(phyreg->phy_lp2hs_time); set_MIPIDSI_PHY_TMR_CFG_phy_hs2lp_time(phyreg->phy_hs2lp_time); - set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clklp2hs_time(phyreg->phy_clklp2hs_time); - set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clkhs2lp_time(phyreg->phy_clkhs2lp_time); + set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clklp2hs_time( + phyreg->phy_clklp2hs_time); + set_MIPIDSI_PHY_TMR_LPCLK_CFG_phy_clkhs2lp_time( + phyreg->phy_clkhs2lp_time); set_MIPIDSI_PHY_TMR_clk_to_data_delay(phyreg->clk_to_data_delay); set_MIPIDSI_PHY_TMR_data_to_clk_delay(phyreg->data_to_clk_delay); - /* - * 3. Select the Video Transmission Mode: - * This defines how the processor requires the video line to be - * transported through the DSI link. - */ + set_MIPIDSI_VID_MODE_CFG_frame_bta_ack_en(0); set_MIPIDSI_VID_MODE_CFG_vid_mode_type(phyreg->burst_mode); set_MIPIDSI_LPCLK_CTRL_auto_clklane_ctrl(0); /* for dsi read */ set_MIPIDSI_PCKHDL_CFG_bta_en(0); - /* Enable EOTP TX; Enable EDPI, ALLOWED_CMD_SIZE = 720*/ + /* Enable EOTP TX; Enable EDPI */ if (dsi->mode_flags == MIPI_DSI_MODE_VIDEO) set_MIPIDSI_EDPI_CMD_SIZE(dsi->vm.hactive); /*------------DSI and D-PHY Initialization-----------------*/ - /* switch to video mode */ + set_MIPIDSI_MODE_CFG(MIPIDSI_VIDEO_MODE); - /* enable generate High Speed clock */ set_MIPIDSI_LPCLK_CTRL_phy_txrequestclkhs(1); - /* Waking up Core */ set_MIPIDSI_PWR_UP_shutdownz(1); + DRM_INFO("%s , exit success!\n", __func__); - DRM_DEBUG_DRIVER("exit success.\n"); + return 0; } @@ -774,14 +744,12 @@ static void hisi_dsi_enable(struct hisi_dsi *dsi) static void hisi_dsi_disable(struct hisi_dsi *dsi) { DRM_DEBUG_DRIVER("enter.\n"); - /*reset Core*/ + set_MIPIDSI_PWR_UP_shutdownz(0); - /* disable generate High Speed clock */ set_MIPIDSI_LPCLK_CTRL_phy_txrequestclkhs(0); - /* shutdown d_phy */ set_MIPIDSI_PHY_RSTZ(0); - /* mipi dphy clock disable */ clk_disable_unprepare(dsi->dsi_cfg_clk); + DRM_DEBUG_DRIVER("exit success.\n"); } @@ -864,10 +832,9 @@ static void hisi_drm_encoder_mode_set(struct drm_encoder *encoder, vm->hsync_len = mode->hsync_end - mode->hsync_start; dsi->lanes = 3 + !!(vm->pixelclock >= 115000); - mipi_init(dsi); - /* laneBitRate >= pixelClk * bpp /lanes */ - dphy_freq_kHz = vm->pixelclock /* mode->clock */ * 24 / dsi->lanes; + dphy_freq_kHz = vm->pixelclock * 24 / dsi->lanes; + /* this avoids a less-compatible DSI rate with 1.2GHz px PLL */ if (dphy_freq_kHz == 600000) dphy_freq_kHz = 640000; From b489685dae3948b9d540a257f6f1c4a8bc32f0dc Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 30 Jun 2015 11:23:22 +0800 Subject: [PATCH 20/28] adv7511 force hpd and fallback mode if no hpd at boot If the HDMI connector is out during boot, this forces the HDMI up anyway and causes it to select the fallback (720p) mode since no EDID is coming. It means the HDMI is configured and working for 720p all the time. This provides a neat way to force the fallback 720p mode by unplugging until boot is complete, then plugging the monitor to find whatever booted (Xorg, framebuffer console) configured fully for the fallback mode. Signed-off-by: Andy Green --- drivers/gpu/drm/i2c/adv7511.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index a825bd6fa68fe7..916964f8508107 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -27,6 +27,9 @@ /* uncomment to enable Internal Timing Generator + DE */ //#define ITG +/* uncomment to force plug detect always */ +#define FORCE_HPD + /* ADI recommended values for proper operation. */ static const struct reg_default adv7511_fixed_registers[] = { { 0x98, 0x03 }, @@ -504,8 +507,7 @@ static void adv7511_power_on(struct adv7511 *adv7511) * first few seconds after enabling the output. */ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, - ADV7511_REG_POWER2_HDP_SRC_MASK, - ADV7511_REG_POWER2_HDP_SRC_NONE); + ADV7511_REG_POWER2_HDP_SRC_MASK, 0x40); /* * Most of the registers are reset during power down or when HPD is low. @@ -533,7 +535,7 @@ static void adv7511_power_off(struct adv7511 *adv7511) /* ----------------------------------------------------------------------------- * Interrupt and hotplug detection */ - +#ifndef FORCE_HPD static bool adv7511_hpd(struct adv7511 *adv7511) { unsigned int irq0; @@ -551,6 +553,7 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } +#endif static int adv7511_irq_process(struct adv7511 *adv7511) { @@ -679,8 +682,7 @@ static int adv7511_get_modes(struct adv7511 *adv7511, /* Reading the EDID only works if the device is powered */ if (!adv7511->powered) { regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, - ADV7511_REG_POWER2_HDP_SRC_MASK, - ADV7511_REG_POWER2_HDP_SRC_NONE); + ADV7511_REG_POWER2_HDP_SRC_MASK, 0x40); regmap_write(adv7511->regmap, ADV7511_REG_INT(0), ADV7511_INT0_EDID_READY); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), @@ -716,19 +718,22 @@ adv7511_detect(struct adv7511 *adv7511, { enum drm_connector_status status; unsigned int val; - bool hpd; + bool hpd = true; int ret; ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); if (ret < 0) return connector_status_disconnected; +#ifndef FORCE_HPD if (val & ADV7511_STATUS_HPD) +#endif status = connector_status_connected; +#ifndef FORCE_HPD else status = connector_status_disconnected; - hpd = adv7511_hpd(adv7511); +#endif /* 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 @@ -740,11 +745,12 @@ adv7511_detect(struct adv7511 *adv7511, adv7511_get_modes(adv7511, connector); if (adv7511->status == connector_status_connected) status = connector_status_disconnected; +#ifndef FORCE_HPD } else { /* Renable HDP sensing */ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, - ADV7511_REG_POWER2_HDP_SRC_MASK, - ADV7511_REG_POWER2_HDP_SRC_BOTH); + ADV7511_REG_POWER2_HDP_SRC_MASK, 0); +#endif } adv7511->status = status; From 0b3a0a3de6f24b5ed189ef565650b84129b6cc35 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 4 Jul 2015 10:14:46 +0800 Subject: [PATCH 21/28] clk hi6220 allow dt to override fixed clk rates Signed-off-by: Andy Green --- arch/arm64/boot/dts/hi6220.dtsi | 7 +++++++ drivers/clk/hisilicon/clk-hi6220.c | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index 205689f2902f79..57ae382d9c7280 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -159,6 +159,13 @@ compatible = "hisilicon,hi6220-clock-ao"; reg = <0 0x1000>; #clock-cells = <1>; + /* any entries here override hardcoded clk rates */ + clock-frequency = <1200000000>, + <1200000000>, + <1200000000>; + clock-names = "syspll", + "media_syspll", + "ddr_sel_src"; }; }; diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 4ea7e975f2b2bd..6f22156b3b96c7 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -77,10 +77,34 @@ static struct hisi_clock_data *clk_data_ao; static void __init hi6220_clk_ao_init(struct device_node *np) { + const char *p; + int n = 0, m; + u32 u; + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); if (!clk_data_ao) return; + /* override default freqs with any mentioned in DT */ + while (1) { + if (of_property_read_string_index(np, "clock-names", n, &p)) + break; + for (m = 0; m < ARRAY_SIZE(hi6220_fixed_rate_clks); m++) { + if (strcmp(hi6220_fixed_rate_clks[m].name, p)) + continue; + if (of_property_read_u32_index(np, "clock-frequency", + n, &u)) + break; + pr_debug("%s: fixed clk %s from %lu to %u\n", __func__, + hi6220_fixed_rate_clks[m].name, + hi6220_fixed_rate_clks[m].fixed_rate, u); + hi6220_fixed_rate_clks[m].fixed_rate = u; + } + if (m != ARRAY_SIZE(hi6220_fixed_rate_clks)) + break; + n++; + } + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); From 99ac3efc7486a5e3e3f37b3d80722ae5b0339441 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 4 Jul 2015 10:35:37 +0800 Subject: [PATCH 22/28] clk hi6220 HACK force fixed clocks to 1.1904GHz in dtsi Bootloader should rewrite these to actual setting. When it does, remove this patch. Signed-off-by: Andy Green --- arch/arm64/boot/dts/hi6220.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index 57ae382d9c7280..3db46359d52474 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -160,9 +160,9 @@ reg = <0 0x1000>; #clock-cells = <1>; /* any entries here override hardcoded clk rates */ - clock-frequency = <1200000000>, - <1200000000>, - <1200000000>; + clock-frequency = <1190400000>, + <1190400000>, + <1190400000>; clock-names = "syspll", "media_syspll", "ddr_sel_src"; From c11a59555db5b1c2307fd28a7af88088b44fa3b7 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Thu, 23 Jul 2015 10:59:38 +0800 Subject: [PATCH 23/28] Revert "clk hi6220 HACK force fixed clocks to 1.1904GHz in dtsi" This reverts commit 99ac3efc7486a5e3e3f37b3d80722ae5b0339441. --- arch/arm64/boot/dts/hi6220.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index f123c908dbd611..f57ddd31f760de 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -151,9 +151,9 @@ reg = <0 0x1000>; #clock-cells = <1>; /* any entries here override hardcoded clk rates */ - clock-frequency = <1190400000>, - <1190400000>, - <1190400000>; + clock-frequency = <1200000000>, + <1200000000>, + <1200000000>; clock-names = "syspll", "media_syspll", "ddr_sel_src"; From bb1a72937ce76739eb29c3134880496d48915b58 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Thu, 23 Jul 2015 10:59:55 +0800 Subject: [PATCH 24/28] Revert "clk hi6220 allow dt to override fixed clk rates" This reverts commit 0b3a0a3de6f24b5ed189ef565650b84129b6cc35. --- arch/arm64/boot/dts/hi6220.dtsi | 7 ------- drivers/clk/hisilicon/clk-hi6220.c | 24 ------------------------ 2 files changed, 31 deletions(-) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index f57ddd31f760de..0fa2b60ab28c82 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -150,13 +150,6 @@ compatible = "hisilicon,hi6220-clock-ao"; reg = <0 0x1000>; #clock-cells = <1>; - /* any entries here override hardcoded clk rates */ - clock-frequency = <1200000000>, - <1200000000>, - <1200000000>; - clock-names = "syspll", - "media_syspll", - "ddr_sel_src"; }; }; diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 6f22156b3b96c7..4ea7e975f2b2bd 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -77,34 +77,10 @@ static struct hisi_clock_data *clk_data_ao; static void __init hi6220_clk_ao_init(struct device_node *np) { - const char *p; - int n = 0, m; - u32 u; - clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); if (!clk_data_ao) return; - /* override default freqs with any mentioned in DT */ - while (1) { - if (of_property_read_string_index(np, "clock-names", n, &p)) - break; - for (m = 0; m < ARRAY_SIZE(hi6220_fixed_rate_clks); m++) { - if (strcmp(hi6220_fixed_rate_clks[m].name, p)) - continue; - if (of_property_read_u32_index(np, "clock-frequency", - n, &u)) - break; - pr_debug("%s: fixed clk %s from %lu to %u\n", __func__, - hi6220_fixed_rate_clks[m].name, - hi6220_fixed_rate_clks[m].fixed_rate, u); - hi6220_fixed_rate_clks[m].fixed_rate = u; - } - if (m != ARRAY_SIZE(hi6220_fixed_rate_clks)) - break; - n++; - } - hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); From 9f00a8820a5dd36f7afa284de6f8d3337d3ade9e Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 17 Jun 2015 18:21:28 +0800 Subject: [PATCH 25/28] clk: hi6220 syspll clk 1.2G to 1.190494208GHz Signed-off-by: Andy Green Signed-off-by: Guodong Xu --- drivers/clk/hisilicon/clk-hi6220.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 4ea7e975f2b2bd..bdbcbea9fb9f65 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -35,9 +35,9 @@ static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, - { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, - { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, - { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1190494208,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1190494208,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1190494208,}, { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, }; From 92cbe684546027b41e84948d1ca312000f67b772 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 14 Jul 2015 19:59:01 +0800 Subject: [PATCH 26/28] drm: hisi: generate 'change' uevent for sysrq g mode hack Signed-off-by: Guodong Xu --- drivers/gpu/drm/drm_crtc_helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index fba41a5013b44c..164dfb34799eb2 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -228,6 +228,7 @@ static void modehack_handler(struct work_struct *work) struct drm_connector *connector = NULL, *connector1; struct drm_display_mode *mode1, *mode_first = NULL, *mode2 = NULL; bool next = false; + char *envp[2]; if (!modehack_set.crtc) return; @@ -273,6 +274,10 @@ static void modehack_handler(struct work_struct *work) drm_crtc_helper_set_config(&modehack_set); drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + envp[0] = "SOURCE=hotkey"; + envp[1] = NULL; + kobject_uevent_env(&connector->kdev->kobj, KOBJ_CHANGE, envp); + bail: drm_modeset_unlock_all(modehack_set.crtc->dev); } From 72b51f1cebe72f7255f2d5e2e99e86992328f0d5 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 15 Jul 2015 22:13:01 +0800 Subject: [PATCH 27/28] clk: hi6220: syspll is set by bootloader. Read from registers during driver init. SYSPLL is set by bootloader. Hikey has two different bootloader rate settings due to historical reasons. So, in order to compatible to both bootloader settings, in kernel space, when clk init, we read the actual setting from registers. Previously, this is assumed static and hardcoded in a static data structure. Signed-off-by: Guodong Xu --- drivers/clk/hisilicon/clk-hi6220.c | 36 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index bdbcbea9fb9f65..976394f9550bf1 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -10,15 +10,15 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include +#include #include #include #include #include -#include #include @@ -36,8 +36,8 @@ static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1190494208,}, - { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1190494208,}, - { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1190494208,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, }; @@ -73,14 +73,42 @@ static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { { HI6220_RTC1_PCLK, "rtc1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 26, 0, }, }; +#define SOC_PERI_SCTRL_BASE_ADDR 0xF7030000 /* peri ctrl base addr */ +#define SC_PERIPH_CTRL14 0x02C +#define SC_PERIPH_STAT1 0x094 + static struct hisi_clock_data *clk_data_ao; static void __init hi6220_clk_ao_init(struct device_node *np) { + void __iomem *peri_base; + unsigned int syspll_freq; + int i; + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); if (!clk_data_ao) return; + /* SYSPLL is set by bootloader. Read it */ + peri_base = ioremap(SOC_PERI_SCTRL_BASE_ADDR, 0x1000); + /* 0x2101 means to calculate clk_sys_pll */ + writel(0x2101, peri_base + SC_PERIPH_CTRL14); + /* read back the calculated value */ + syspll_freq = readl(peri_base + SC_PERIPH_STAT1); + pr_debug("SYSPLL: syspll_freq is read: %d\n", syspll_freq); + if (syspll_freq == 0x00020000 || syspll_freq == 0) + syspll_freq = 1200000000; + pr_debug("SYSPLL: syspll_freq will be set to: %d\n", syspll_freq); + + for (i = 0; i < ARRAY_SIZE(hi6220_fixed_rate_clks); i++) { + if (hi6220_fixed_rate_clks[i].id == HI6220_PLL_SYS) { + hi6220_fixed_rate_clks[i].fixed_rate = syspll_freq; + printk("SYSPLL: modified fix_rate[%d], id=%d, f=%d\n", \ + i, hi6220_fixed_rate_clks[i].id, syspll_freq); + break; + } + } + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); From 38f1300e841a9126a807b03dc9bf93a0f80cf4aa Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Thu, 23 Jul 2015 11:39:29 +0800 Subject: [PATCH 28/28] clk: hi6220: workaround to set syspll, medpll, ddrsrc to the same freq According to Hi6220 SoC designer, when you change syspll to 1.19GHz, medpll and ddrsrc should remain as 1.2GHz. When I test with fastboot bootloader that doesn't make any problem. However, when test with ATF/UEFI, I found HDMI cannot work properly if I set syspll, medpll and ddrsrc to different values. This is a workaround patch which keeps all three clks at the same value, either 1.2GHz or 1.19GHz according to bootloader's setting. With this patch, no regression issues are found so far on both fastboot and ATF/UEFI. Signed-off-by: Guodong Xu --- drivers/clk/hisilicon/clk-hi6220.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 976394f9550bf1..c1e243273fccce 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -36,8 +36,8 @@ static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1190494208,}, - { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, - { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1190494208,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1190494208,}, { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, }; @@ -95,16 +95,21 @@ static void __init hi6220_clk_ao_init(struct device_node *np) writel(0x2101, peri_base + SC_PERIPH_CTRL14); /* read back the calculated value */ syspll_freq = readl(peri_base + SC_PERIPH_STAT1); - pr_debug("SYSPLL: syspll_freq is read: %d\n", syspll_freq); + pr_info("SYSPLL: syspll_freq is read: 0x%x, %d\n", syspll_freq, \ + syspll_freq); if (syspll_freq == 0x00020000 || syspll_freq == 0) syspll_freq = 1200000000; - pr_debug("SYSPLL: syspll_freq will be set to: %d\n", syspll_freq); + pr_info("SYSPLL: set syspll medpll ddrsrc: %d\n", syspll_freq); for (i = 0; i < ARRAY_SIZE(hi6220_fixed_rate_clks); i++) { - if (hi6220_fixed_rate_clks[i].id == HI6220_PLL_SYS) { + switch (hi6220_fixed_rate_clks[i].id) { + case HI6220_PLL_SYS: + case HI6220_PLL_SYS_MEDIA: + case HI6220_DDR_SRC: hi6220_fixed_rate_clks[i].fixed_rate = syspll_freq; - printk("SYSPLL: modified fix_rate[%d], id=%d, f=%d\n", \ + pr_info("SYSPLL: modified fix_rate[%d], id=%d, f=%d\n", \ i, hi6220_fixed_rate_clks[i].id, syspll_freq); + default: break; } }