From 8ebd0d988d9ed23c46322797108412f35149199a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Wed, 19 Aug 2015 16:14:09 +0530 Subject: drm/mipi_dsi: refactor device creation Create a helper function mipi_dsi_device_new which takes in struct mipi_dsi_device_info and the mipi_dsi_host. This will be called by of_mipi_dsi_device_add. Instead of calling device_initialize and device_add separately, merge it into a single device_register call. This will remove the need of having two separate funcs mipi_dsi_device_alloc and mipi_dsi_device_add. The reason for creating mipi_dsi_device_new is that it can also be used as a standalone way for creating a dsi device that isn't available via DT. Signed-off-by: Archit Taneja --- drivers/gpu/drm/drm_mipi_dsi.c | 74 ++++++++++++++++++++---------------------- include/drm/drm_mipi_dsi.h | 5 +++ 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 2d5ca8eec13a..583b3bc10495 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -102,9 +102,25 @@ static const struct device_type mipi_dsi_device_type = { .release = mipi_dsi_dev_release, }; -static struct mipi_dsi_device *mipi_dsi_device_alloc(struct mipi_dsi_host *host) +struct mipi_dsi_device_info { + char name[DSI_DEV_NAME_SIZE]; + u32 reg; + struct device_node *node; +}; + +static struct mipi_dsi_device * +mipi_dsi_device_new(struct mipi_dsi_host *host, + struct mipi_dsi_device_info *info) { + struct device *dev = host->dev; struct mipi_dsi_device *dsi; + int r; + + if (info->reg > 3) { + dev_err(dev, "dsi device %s has invalid channel value: %u\n", + info->name, info->reg); + return ERR_PTR(-EINVAL); + } dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); if (!dsi) @@ -114,61 +130,43 @@ static struct mipi_dsi_device *mipi_dsi_device_alloc(struct mipi_dsi_host *host) dsi->dev.bus = &mipi_dsi_bus_type; dsi->dev.parent = host->dev; dsi->dev.type = &mipi_dsi_device_type; + dsi->dev.of_node = info->node; + dsi->channel = info->reg; + strlcpy(dsi->name, info->name, sizeof(dsi->name)); - device_initialize(&dsi->dev); + dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), info->reg); - return dsi; -} - -static int mipi_dsi_device_add(struct mipi_dsi_device *dsi) -{ - struct mipi_dsi_host *host = dsi->host; - - dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), dsi->channel); + r = device_register(&dsi->dev); + if (r) { + kfree(dsi); + return ERR_PTR(r); + } - return device_add(&dsi->dev); + return dsi; } static struct mipi_dsi_device * of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) { - struct mipi_dsi_device *dsi; struct device *dev = host->dev; + struct mipi_dsi_device_info info = { }; int ret; - u32 reg; - - ret = of_property_read_u32(node, "reg", ®); - if (ret) { - dev_err(dev, "device node %s has no valid reg property: %d\n", - node->full_name, ret); - return ERR_PTR(-EINVAL); - } - if (reg > 3) { - dev_err(dev, "device node %s has invalid reg property: %u\n", - node->full_name, reg); + if (of_modalias_node(node, info.name, sizeof(info.name)) < 0) { + dev_err(dev, "modalias failure on %s\n", node->full_name); return ERR_PTR(-EINVAL); } - dsi = mipi_dsi_device_alloc(host); - if (IS_ERR(dsi)) { - dev_err(dev, "failed to allocate DSI device %s: %ld\n", - node->full_name, PTR_ERR(dsi)); - return dsi; - } - - dsi->dev.of_node = of_node_get(node); - dsi->channel = reg; - - ret = mipi_dsi_device_add(dsi); + ret = of_property_read_u32(node, "reg", &info.reg); if (ret) { - dev_err(dev, "failed to add DSI device %s: %d\n", + dev_err(dev, "device node %s has no valid reg property: %d\n", node->full_name, ret); - kfree(dsi); - return ERR_PTR(ret); + return ERR_PTR(-EINVAL); } - return dsi; + info.node = of_node_get(node); + + return mipi_dsi_device_new(host, &info); } int mipi_dsi_host_register(struct mipi_dsi_host *host) diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index f1d8d0dbb4f1..5a35c6fd9b09 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -139,10 +139,13 @@ enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB565, }; +#define DSI_DEV_NAME_SIZE 20 + /** * struct mipi_dsi_device - DSI peripheral device * @host: DSI host for this peripheral * @dev: driver model device node for this peripheral + * @name: name of the dsi peripheral * @channel: virtual channel assigned to the peripheral * @format: pixel format for video mode * @lanes: number of active data lanes @@ -152,6 +155,8 @@ struct mipi_dsi_device { struct mipi_dsi_host *host; struct device dev; + char name[DSI_DEV_NAME_SIZE]; + unsigned int channel; unsigned int lanes; enum mipi_dsi_pixel_format format; -- cgit v1.2.3 From 81f6262e3903e43da0d4de6eb6dd8884264c6c83 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Wed, 19 Aug 2015 17:31:23 +0530 Subject: drm/mipi_dsi: check for used channels We don't check whether a previous mipi_dsi_device under the same host shares the same virtual channel. Before registering, check if any of the registered devices doesn't already have the same virtual channel. Signed-off-by: Archit Taneja --- drivers/gpu/drm/drm_mipi_dsi.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 583b3bc10495..3a507fe534fd 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -108,6 +108,22 @@ struct mipi_dsi_device_info { struct device_node *node; }; +static int __dsi_check_chan_busy(struct device *dev, void *data) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); + u32 reg = *(u32 *) data; + + if (dsi && dsi->channel == reg) + return -EBUSY; + + return 0; +} + +static int mipi_dsi_check_chan_busy(struct mipi_dsi_host *host, u32 reg) +{ + return device_for_each_child(&host->dev, ®, __dsi_check_chan_busy); +} + static struct mipi_dsi_device * mipi_dsi_device_new(struct mipi_dsi_host *host, struct mipi_dsi_device_info *info) @@ -136,13 +152,18 @@ mipi_dsi_device_new(struct mipi_dsi_host *host, dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), info->reg); + r = mipi_dsi_check_chan_busy(host, info->reg); + if (r) + goto err; + r = device_register(&dsi->dev); - if (r) { - kfree(dsi); - return ERR_PTR(r); - } + if (r) + goto err; return dsi; +err: + kfree(dsi); + return ERR_PTR(r); } static struct mipi_dsi_device * -- cgit v1.2.3 From e5819a24755dbb8755bde267e482d2f5d436e2e5 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 7 Sep 2015 10:38:51 +0530 Subject: drm/mipi_dsi: Create dummy DSI devices We can have devices where the data bus is MIPI DSI, but the control bus is something else (i2c, spi etc). A typical example is i2c controlled encoder bridge chips. Such devices too require passing DSI specific parameters (number of data lanes, DSI mode flags, color format etc) to their DSI host. For a device that isn't 'mipi_dsi_device', there is no way of passing such parameters. Provide the option of creating a dummy DSI device. The main purpose of this would be to attach to a DSI host by calling mipi_dsi_attach, and pass DSI params. Create mipi_dsi_new_dummy for creating a dummy dsi device. The driver calling this needs to be aware of the mipi_dsi_host it wants to attach to, and also the DSI virtual channel the DSI device intends to use. Signed-off-by: Archit Taneja --- drivers/gpu/drm/drm_mipi_dsi.c | 48 +++++++++++++++++++++++++++++++++++++++--- include/drm/drm_mipi_dsi.h | 2 ++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 3a507fe534fd..2d642752e1a5 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -45,9 +45,26 @@ * subset of the MIPI DCS command set. */ +static const struct device_type mipi_dsi_device_type; + static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv) { - return of_driver_match_device(dev, drv); + struct mipi_dsi_device *dsi; + + dsi = dev->type == &mipi_dsi_device_type ? + to_mipi_dsi_device(dev) : NULL; + + if (!dsi) + return 0; + + if (of_driver_match_device(dev, drv)) + return 1; + + if (!strcmp(drv->name, "mipi_dsi_dummy") && + !strcmp(dsi->name, "dummy")) + return 1; + + return 0; } static const struct dev_pm_ops mipi_dsi_device_pm_ops = { @@ -121,7 +138,7 @@ static int __dsi_check_chan_busy(struct device *dev, void *data) static int mipi_dsi_check_chan_busy(struct mipi_dsi_host *host, u32 reg) { - return device_for_each_child(&host->dev, ®, __dsi_check_chan_busy); + return device_for_each_child(host->dev, ®, __dsi_check_chan_busy); } static struct mipi_dsi_device * @@ -190,6 +207,25 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) return mipi_dsi_device_new(host, &info); } +static struct mipi_dsi_driver dummy_dsi_driver = { + .driver.name = "mipi_dsi_dummy", +}; + +struct mipi_dsi_device *mipi_dsi_new_dummy(struct mipi_dsi_host *host, u32 reg) +{ + struct mipi_dsi_device_info info = { "dummy", reg, NULL, }; + + return mipi_dsi_device_new(host, &info); +} +EXPORT_SYMBOL(mipi_dsi_new_dummy); + +void mipi_dsi_unregister_device(struct mipi_dsi_device *dsi) +{ + if (dsi) + device_unregister(&dsi->dev); +} +EXPORT_SYMBOL(mipi_dsi_unregister_device); + int mipi_dsi_host_register(struct mipi_dsi_host *host) { struct device_node *node; @@ -943,7 +979,13 @@ EXPORT_SYMBOL(mipi_dsi_driver_unregister); static int __init mipi_dsi_bus_init(void) { - return bus_register(&mipi_dsi_bus_type); + int ret; + + ret = bus_register(&mipi_dsi_bus_type); + if (ret < 0) + return ret; + + return mipi_dsi_driver_register(&dummy_dsi_driver); } postcore_initcall(mipi_dsi_bus_init); diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 5a35c6fd9b09..f95c8ef3003e 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -168,6 +168,8 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev) return container_of(dev, struct mipi_dsi_device, dev); } +struct mipi_dsi_device *mipi_dsi_new_dummy(struct mipi_dsi_host *host, u32 reg); +void mipi_dsi_unregister_device(struct mipi_dsi_device *dsi); struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np); int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); -- cgit v1.2.3 From 842822d328a5c25d587cb69777d9d2b9cf860bf9 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 4 Sep 2015 16:00:55 +0530 Subject: drm/mipi_dsi: Get DSI host by DT device node mipi_dsi_devices are inherently aware of their host because they share a parent-child hierarchy in the device tree. Non-dsi drivers that create a dummy dsi device don't have this data. In order to get this information, they require to a phandle to the dsi host in the device tree. Maintain a list of all the hosts DSI that are currently registered. This list will be used to find the mipi_dsi_host corresponding to the device_node passed in of_find_mipi_dsi_host_by_node. Signed-off-by: Archit Taneja --- drivers/gpu/drm/drm_mipi_dsi.c | 30 ++++++++++++++++++++++++++++++ include/drm/drm_mipi_dsi.h | 2 ++ 2 files changed, 32 insertions(+) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 2d642752e1a5..e487f56ffc4c 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -226,6 +226,28 @@ void mipi_dsi_unregister_device(struct mipi_dsi_device *dsi) } EXPORT_SYMBOL(mipi_dsi_unregister_device); +static DEFINE_MUTEX(host_lock); +static LIST_HEAD(host_list); + +struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node) +{ + struct mipi_dsi_host *host; + + mutex_lock(&host_lock); + + list_for_each_entry(host, &host_list, list) { + if (host->dev->of_node == node) { + mutex_unlock(&host_lock); + return host; + } + } + + mutex_unlock(&host_lock); + + return NULL; +} +EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node); + int mipi_dsi_host_register(struct mipi_dsi_host *host) { struct device_node *node; @@ -237,6 +259,10 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host) of_mipi_dsi_device_add(host, node); } + mutex_lock(&host_lock); + list_add_tail(&host->list, &host_list); + mutex_unlock(&host_lock); + return 0; } EXPORT_SYMBOL(mipi_dsi_host_register); @@ -253,6 +279,10 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv) void mipi_dsi_host_unregister(struct mipi_dsi_host *host) { device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn); + + mutex_lock(&host_lock); + list_del_init(&host->list); + mutex_unlock(&host_lock); } EXPORT_SYMBOL(mipi_dsi_host_unregister); diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index f95c8ef3003e..fc1df05aeb85 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -100,10 +100,12 @@ struct mipi_dsi_host_ops { struct mipi_dsi_host { struct device *dev; const struct mipi_dsi_host_ops *ops; + struct list_head list; }; int mipi_dsi_host_register(struct mipi_dsi_host *host); void mipi_dsi_host_unregister(struct mipi_dsi_host *host); +struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node); /* DSI mode flags */ -- cgit v1.2.3 From de7cbb30f862b3dd2ff6bf21b8112d8465fc8fe5 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 3 Jul 2015 11:41:10 +0530 Subject: drm/i2c: adv7511: Fix mutex deadlock when interrupts are disabled When the adv7511 i2c client doesn't have an interrupt line, we observe a deadlock on caused by trying to lock drm device's mode_config.mutex twice in the same context. Here is the sequence that causes it: ioctl DRM_IOCTL_MODE_GETCONNECTOR from userspace drm_mode_getconnector (acquires mode_config mutex) connector->fill_modes() drm_helper_probe_single_connector_modes connector_funcs->get_modes adv7511_encoder_get_modes adv7511_get_edid_block adv7511_irq_process drm_helper_hpd_irq_event (acquires mode_config mutex again) In adv7511_irq_process, don't call drm_helper_hpd_irq_event when not called from interrupt context. It doesn't serve any purpose there anyway. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 00416f23b5cb..d3f0f6b422ce 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -422,7 +422,7 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } -static int adv7511_irq_process(struct adv7511 *adv7511) +static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { unsigned int irq0, irq1; int ret; @@ -438,7 +438,7 @@ 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 && adv7511->encoder) + if (process_hpd && irq0 & ADV7511_INT0_HDP && adv7511->encoder) drm_helper_hpd_irq_event(adv7511->encoder->dev); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { @@ -456,7 +456,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) struct adv7511 *adv7511 = devid; int ret; - ret = adv7511_irq_process(adv7511); + ret = adv7511_irq_process(adv7511, true); return ret < 0 ? IRQ_NONE : IRQ_HANDLED; } @@ -473,7 +473,7 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout) adv7511->edid_read, msecs_to_jiffies(timeout)); } else { for (; timeout > 0; timeout -= 25) { - ret = adv7511_irq_process(adv7511); + ret = adv7511_irq_process(adv7511, false); if (ret < 0) break; -- cgit v1.2.3 From f60074b3eb5115cefbf7a701a339c501a5c03540 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 8 Jul 2015 12:27:05 +0530 Subject: drm/i2c: adv7511: Initial support for adv7533 ADV7533 is a DSI to HDMI encoder chip. It is a derivative of ADV7511, with additional blocks to translate input DSI data to parallel RGB data. Besides the ADV7511 i2c register map, it has additional registers that require to be configured to activate the DSI blocks. Use DT compatible strings to populate the adv7533 type enum. Add minimal register configurations belonging to the DSI/CEC register map. Signed-off-by: Lars-Peter Clausen --- drivers/gpu/drm/i2c/adv7511.c | 155 +++++++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index d3f0f6b422ce..3da9c3ea0380 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -20,12 +20,18 @@ #include "adv7511.h" +enum adv7511_type { + ADV7511, + ADV7533, +}; + struct adv7511 { struct i2c_client *i2c_main; struct i2c_client *i2c_edid; + struct i2c_client *i2c_cec; struct regmap *regmap; - struct regmap *packet_memory_regmap; + struct regmap *regmap_cec; enum drm_connector_status status; bool powered; @@ -46,6 +52,8 @@ struct adv7511 { struct edid *edid; struct gpio_desc *gpio_pd; + + enum adv7511_type type; }; static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) @@ -66,6 +74,23 @@ static const struct reg_sequence 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 }, +}; + /* ----------------------------------------------------------------------------- * Register access */ @@ -158,6 +183,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 */ @@ -358,6 +392,25 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; } +static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) +{ + if (adv7511->type != ADV7533) + return; + + if (adv7511->powered) { + /* set number of dsi lanes (hardcoded to 4 for now) */ + regmap_write(adv7511->regmap_cec, 0x1c, 0x4 << 4); + /* disable internal timing generator */ + regmap_write(adv7511->regmap_cec, 0x27, 0x0b); + /* enable hdmi */ + regmap_write(adv7511->regmap_cec, 0x03, 0x89); + /* disable test mode */ + regmap_write(adv7511->regmap_cec, 0x55, 0x00); + } else { + regmap_write(adv7511->regmap_cec, 0x03, 0x0b); + } +} + static void adv7511_power_on(struct adv7511 *adv7511) { adv7511->current_edid_segment = -1; @@ -387,6 +440,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 +453,8 @@ static void adv7511_power_off(struct adv7511 *adv7511) regcache_mark_dirty(adv7511->regmap); adv7511->powered = false; + + adv7511_dsi_receiver_dpms(adv7511); } /* ----------------------------------------------------------------------------- @@ -567,6 +624,9 @@ static int adv7511_get_modes(struct drm_encoder *encoder, /* 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), @@ -770,8 +830,6 @@ static int adv7511_parse_dt(struct device_node *np, 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) @@ -853,6 +911,15 @@ static const int edid_i2c_addr = 0x7e; static const int packet_i2c_addr = 0x70; static const int cec_i2c_addr = 0x78; +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); + static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct adv7511_link_config link_config; @@ -871,9 +938,22 @@ 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 (ret) - return ret; + if (dev->of_node) { + const struct of_device_id *of_id; + + of_id = of_match_node(adv7511_of_ids, dev->of_node); + adv7511->type = (enum adv7511_type) of_id->data; + } else { + adv7511->type = id->driver_data; + } + + memset(&link_config, 0, sizeof(link_config)); + + if (adv7511->type == ADV7511) { + 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 @@ -897,10 +977,19 @@ 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, - ARRAY_SIZE(adv7511_fixed_registers)); - if (ret) - return ret; + if (adv7511->type == ADV7511) { + ret = regmap_register_patch(adv7511->regmap, + adv7511_fixed_registers, + ARRAY_SIZE(adv7511_fixed_registers)); + 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 +1002,27 @@ 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 +1031,7 @@ 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; } /* CEC is unused for now */ @@ -932,11 +1042,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) i2c_set_clientdata(i2c, adv7511); - adv7511_set_link_config(adv7511, &link_config); + if (adv7511->type == ADV7511) + 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 +1059,7 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + i2c_unregister_device(adv7511->i2c_cec); i2c_unregister_device(adv7511->i2c_edid); kfree(adv7511->edid); @@ -968,21 +1082,14 @@ static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, } static const struct i2c_device_id adv7511_i2c_ids[] = { - { "adv7511", 0 }, - { "adv7511w", 0 }, - { "adv7513", 0 }, + { "adv7511", ADV7511 }, + { "adv7511w", ADV7511 }, + { "adv7513", ADV7511 }, + { "adv7533", ADV7533 }, { } }; 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 = { -- cgit v1.2.3 From f9ce2a246cd62d38d272bcc11ada3128114b5d66 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Jul 2015 15:55:55 +0530 Subject: drm/i2c: adv7511: Refactor encoder slave functions ADV7511 is represented as an i2c drm slave encoder device. ADV7533, on the other hand, is going be a normal i2c client device creating bridge and connector entities. Move the code in encoder slave functions to generate helpers that are agnostic to the drm object type. These helpers will later also be used by bridge and connecter helper functions. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 80 ++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 3da9c3ea0380..3d5f36edb443 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -612,13 +612,11 @@ 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; @@ -656,21 +654,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; @@ -694,7 +681,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 { @@ -708,8 +695,8 @@ adv7511_encoder_detect(struct drm_encoder *encoder, return status; } -static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) +static int adv7511_mode_valid(struct adv7511 *adv7511, + const struct drm_display_mode *mode) { if (mode->clock > 165000) return MODE_CLOCK_HIGH; @@ -717,11 +704,10 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, 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); unsigned int low_refresh_rate; unsigned int hsync_polarity = 0; unsigned int vsync_polarity = 0; @@ -812,12 +798,60 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, adv7511->f_tmds = mode->clock; } +/* ----------------------------------------------------------------------------- + * Encoder operations + */ + +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) +{ + struct adv7511 *adv7511 = encoder_to_adv7511(encoder); + + return adv7511_mode_valid(adv7511, mode); +} + +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 = { .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, + .get_modes = adv7511_encoder_get_modes, }; /* ----------------------------------------------------------------------------- -- cgit v1.2.3 From 88d6f44d73c41a74d1686dfc32bed47304de4cee Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 3 Jul 2015 15:32:48 +0530 Subject: drm/i2c: adv7511: Add drm_bridge/connector for ADV7533 Create bridge and connector helper functions. These internally refer to the ADV75xx helper functions. The driver registers a drm_bridge object during probe. The bridge, in turn registers a HDMI connector when a user attaches the bridge. Therefore, when the device type is ADV7533, we create bridge and connector entities, and when it's ADV7511, we create a slave encoder as before. Since the i2c driver is still wrapped around by the drm_i2c_slave_encoder struct. We make sure the encoder_init op returns an error when the device type is ADV7533. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 155 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 3d5f36edb443..16f2ebb3d48b 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "adv7511.h" @@ -44,6 +46,9 @@ struct adv7511 { wait_queue_head_t wq; struct drm_encoder *encoder; + struct drm_connector connector; + struct drm_bridge bridge; + bool embedded_sync; enum adv7511_sync_polarity vsync_polarity; enum adv7511_sync_polarity hsync_polarity; @@ -854,6 +859,139 @@ static struct drm_encoder_slave_funcs adv7511_encoder_funcs = { .get_modes = adv7511_encoder_get_modes, }; +/* ----------------------------------------------------------------------------- + * 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 adv7533_connector_get_modes(struct drm_connector *connector) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_get_modes(adv, connector); +} + +static struct drm_encoder * +adv7533_connector_best_encoder(struct drm_connector *connector) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv->bridge.encoder; +} + +static enum drm_mode_status +adv7533_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_mode_valid(adv, mode); +} + +static struct drm_connector_helper_funcs adv7533_connector_helper_funcs = { + .get_modes = adv7533_connector_get_modes, + .best_encoder = adv7533_connector_best_encoder, + .mode_valid = adv7533_connector_mode_valid, +}; + +static enum drm_connector_status +adv7533_connector_detect(struct drm_connector *connector, bool force) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_detect(adv, connector); +} + +static struct drm_connector_funcs adv7533_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = adv7533_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 adv7533_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_on(adv); +} + +static void adv7533_bridge_post_disable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_off(adv); +} + +static void adv7533_bridge_enable(struct drm_bridge *bridge) +{ +} + +static void adv7533_bridge_disable(struct drm_bridge *bridge) +{ +} + +static void adv7533_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_mode_set(adv, mode, adj_mode); +} + +static int adv7533_bridge_attach(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + int ret; + + adv->encoder = bridge->encoder; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + adv->connector.polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(bridge->dev, &adv->connector, + &adv7533_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&adv->connector, + &adv7533_connector_helper_funcs); + drm_connector_register(&adv->connector); + drm_mode_connector_attach_encoder(&adv->connector, adv->encoder); + + drm_helper_hpd_irq_event(adv->connector.dev); + + return ret; +} + +static struct drm_bridge_funcs adv7533_bridge_funcs = { + .pre_enable = adv7533_bridge_pre_enable, + .enable = adv7533_bridge_enable, + .disable = adv7533_bridge_disable, + .post_disable = adv7533_bridge_post_disable, + .mode_set = adv7533_bridge_mode_set, + .attach = adv7533_bridge_attach, +}; + /* ----------------------------------------------------------------------------- * Probe & remove */ @@ -1079,6 +1217,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (adv7511->type == ADV7511) adv7511_set_link_config(adv7511, &link_config); + if (adv7511->type == ADV7533) { + adv7511->bridge.funcs = &adv7533_bridge_funcs; + adv7511->bridge.of_node = dev->of_node; + + ret = drm_bridge_add(&adv7511->bridge); + if (ret) { + dev_err(dev, "failed to add adv7533 bridge\n"); + goto err_i2c_unregister_cec; + } + } + return 0; err_i2c_unregister_cec: @@ -1098,6 +1247,9 @@ static int adv7511_remove(struct i2c_client *i2c) kfree(adv7511->edid); + if (adv7511->type == ADV7533) + drm_bridge_remove(&adv7511->bridge); + return 0; } @@ -1107,6 +1259,9 @@ static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + if (adv7511->type == ADV7533) + return -ENODEV; + encoder->slave_priv = adv7511; encoder->slave_funcs = &adv7511_encoder_funcs; -- cgit v1.2.3 From fc9a99c37e74370f22d54244ee1df08ae16dff1a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 9 Jul 2015 10:47:05 +0530 Subject: drm/i2c: adv7511: Create mipi_dsi_device for ADV7533 In order to pass DSI specific parameters to the DSI host, we need the driver to create a mipi_dsi_device that attaches to the host. Use of_graph helpers to get the DSI host DT node. Create a dummy dsi device using this host. Finally, attach this device to the host. Populate few other DT parameters (number of data lanes etc) that are required for DSI RX to work correctly. Hardcode few other parameters (rgb, embedded_sync) for now. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 106 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 16f2ebb3d48b..f0c2d0b41a91 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include "adv7511.h" @@ -58,6 +60,11 @@ struct adv7511 { struct gpio_desc *gpio_pd; + /* ADV7533 DSI RX related params */ + struct device_node *host_node; + struct mipi_dsi_device *dsi; + u8 num_dsi_lanes; + enum adv7511_type type; }; @@ -403,8 +410,10 @@ static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) return; if (adv7511->powered) { - /* set number of dsi lanes (hardcoded to 4 for now) */ - regmap_write(adv7511->regmap_cec, 0x1c, 0x4 << 4); + struct mipi_dsi_device *dsi = adv7511->dsi; + + /* set number of dsi lanes */ + regmap_write(adv7511->regmap_cec, 0x1c, dsi->lanes << 4); /* disable internal timing generator */ regmap_write(adv7511->regmap_cec, 0x27, 0x0b); /* enable hdmi */ @@ -954,6 +963,48 @@ static void adv7533_bridge_mode_set(struct drm_bridge *bridge, adv7511_mode_set(adv, mode, adj_mode); } +static int adv7533_attach_dsi(struct adv7511 *adv7511) +{ + struct device *dev = &adv7511->i2c_main->dev; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + int ret; + + host = of_find_mipi_dsi_host_by_node(adv7511->host_node); + if (!host) { + dev_err(dev, "failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + /* can adv7533 virtual channel be non-zero? */ + dsi = mipi_dsi_new_dummy(host, 0); + if (IS_ERR(dsi)) { + dev_err(dev, "failed to create dummy dsi device\n"); + ret = PTR_ERR(dsi); + goto err_dsi_device; + } + + adv7511->dsi = dsi; + + dsi->lanes = adv7511->num_dsi_lanes; + 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; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "failed to attach dsi to host\n"); + goto err_dsi_attach; + } + + return 0; + +err_dsi_attach: + mipi_dsi_unregister_device(dsi); +err_dsi_device: + return ret; +} + static int adv7533_bridge_attach(struct drm_bridge *bridge) { struct adv7511 *adv = bridge_to_adv7511(bridge); @@ -980,6 +1031,8 @@ static int adv7533_bridge_attach(struct drm_bridge *bridge) drm_helper_hpd_irq_event(adv->connector.dev); + adv7533_attach_dsi(adv); + return ret; } @@ -1079,6 +1132,41 @@ static int adv7511_parse_dt(struct device_node *np, return 0; } +static int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv7511) +{ + u32 num_lanes; + struct device_node *endpoint; + + of_property_read_u32(np, "adi,dsi-lanes", &num_lanes); + + if (num_lanes < 1 || num_lanes > 4) + return -EINVAL; + + adv7511->num_dsi_lanes = num_lanes; + + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) { + DRM_ERROR("adv dsi input endpoint not found\n"); + return -ENODEV; + } + + adv7511->host_node = of_graph_get_remote_port_parent(endpoint); + if (!adv7511->host_node) { + DRM_ERROR("dsi host node not found\n"); + of_node_put(endpoint); + return -ENODEV; + } + + of_node_put(endpoint); + of_node_put(adv7511->host_node); + + /* TODO: Check if these need to be parsed by DT or not */ + adv7511->rgb = true; + adv7511->embedded_sync = false; + + return 0; +} + static const int edid_i2c_addr = 0x7e; static const int packet_i2c_addr = 0x70; static const int cec_i2c_addr = 0x78; @@ -1121,11 +1209,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) memset(&link_config, 0, sizeof(link_config)); - if (adv7511->type == ADV7511) { + if (adv7511->type == ADV7511) ret = adv7511_parse_dt(dev->of_node, &link_config); - if (ret) - return ret; - } + else + ret = adv7533_parse_dt(dev->of_node, adv7511); + if (ret) + return ret; /* * The power down GPIO is optional. If present, toggle it from active to @@ -1247,8 +1336,11 @@ static int adv7511_remove(struct i2c_client *i2c) kfree(adv7511->edid); - if (adv7511->type == ADV7533) + if (adv7511->type == ADV7533) { + mipi_dsi_detach(adv7511->dsi); + mipi_dsi_unregister_device(adv7511->dsi); drm_bridge_remove(&adv7511->bridge); + } return 0; } -- cgit v1.2.3 From 0c4c0f4a5beff361d5f29761ff0dc27e1ece4f01 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Jun 2015 14:46:14 +0100 Subject: drm/i2c: adv7511: setup CEC registers during power off-power-on sequence This patch sets up the CEC register during power up sequence, the reason to do this because during power dowm the bridge can put audio pins in to low power state. Orignally the issue was detected on ubuntu image, where in after first logout the audio was totally disabled. Reported-by: Nicolas Dechesne Signed-off-by: Srinivas Kandagatla --- drivers/gpu/drm/i2c/adv7511.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index f0c2d0b41a91..b72e5292c1e1 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -453,6 +453,10 @@ static void adv7511_power_on(struct adv7511 *adv7511) */ regcache_sync(adv7511->regmap); + if (adv7511->type == ADV7533) + regmap_register_patch(adv7511->regmap_cec, + adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); adv7511->powered = true; adv7511_dsi_receiver_dpms(adv7511); -- cgit v1.2.3 From 4e2335a83d35fd072b1e44dc68ee48e240624cff Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 12 Jun 2015 21:02:05 +0530 Subject: drm/i2c: adv7511: Use internal timing generator ADV7533 provides an internal timing generator for certain modes that it can't use the DSI clock directly. We've observed that hdmi is more stable with the internal timing generator, especially if there are instabilities in the dsi clock source. The data spec recommends usage of the timing generator too. Use it all the time. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 54 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index b72e5292c1e1..eec5885d6e9e 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -39,6 +39,8 @@ struct adv7511 { enum drm_connector_status status; bool powered; + struct drm_display_mode curr_mode; + unsigned int f_tmds; unsigned int current_edid_segment; @@ -404,6 +406,45 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; } +static void adv7511_dsi_config_tgen(struct adv7511 *adv7511) +{ + struct mipi_dsi_device *dsi = adv7511->dsi; + struct drm_display_mode *mode = &adv7511->curr_mode; + u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */ + unsigned int hsw, hfp, hbp, vsw, vfp, vbp; + + 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; + + /* set pixel clock divider mode */ + regmap_write(adv7511->regmap_cec, 0x16, + clock_div_by_lanes[dsi->lanes - 2] << 3); + + /* 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); +} + static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) { if (adv7511->type != ADV7533) @@ -412,16 +453,23 @@ static void adv7511_dsi_receiver_dpms(struct adv7511 *adv7511) if (adv7511->powered) { struct mipi_dsi_device *dsi = adv7511->dsi; + adv7511_dsi_config_tgen(adv7511); + /* set number of dsi lanes */ regmap_write(adv7511->regmap_cec, 0x1c, dsi->lanes << 4); - /* disable internal timing generator */ - regmap_write(adv7511->regmap_cec, 0x27, 0x0b); + + /* 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); + /* enable hdmi */ regmap_write(adv7511->regmap_cec, 0x03, 0x89); /* disable test mode */ regmap_write(adv7511->regmap_cec, 0x55, 0x00); } else { regmap_write(adv7511->regmap_cec, 0x03, 0x0b); + regmap_write(adv7511->regmap_cec, 0x27, 0x0b); } } @@ -808,6 +856,8 @@ static void adv7511_mode_set(struct adv7511 *adv7511, regmap_update_bits(adv7511->regmap, 0x17, 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + drm_mode_copy(&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. -- cgit v1.2.3 From 2e67cada60373e5b44509ae844f298f927f65678 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 7 Sep 2015 11:20:28 +0530 Subject: drm/i2c: adv7511: Change DSI lanes dynamically Lower modes on ADV7511 require lower number of lanes for correct operation. Switch lanes to 3 when the target mode's pixel clock is less than 80 Mhz. Based on patch by Andy Green Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index eec5885d6e9e..46c1d7d427e7 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -856,6 +856,26 @@ static void adv7511_mode_set(struct adv7511 *adv7511, regmap_update_bits(adv7511->regmap, 0x17, 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + if (adv7511->type == ADV7533 && adv7511->num_dsi_lanes == 4) { + struct mipi_dsi_device *dsi = adv7511->dsi; + int lanes, ret; + + if (adj_mode->clock > 80000) + lanes = 4; + else + lanes = 3; + + if (lanes != dsi->lanes) { + mipi_dsi_detach(dsi); + dsi->lanes = lanes; + ret = mipi_dsi_attach(dsi); + if (ret) { + DRM_ERROR("Failed to change host lanes\n"); + return; + } + } + } + drm_mode_copy(&adv7511->curr_mode, adj_mode); /* -- cgit v1.2.3 From c33eeadba024ca1a7736ac4f6fb500c61626e1a3 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 7 Sep 2015 11:45:02 +0530 Subject: drm/i2c: adv7511: Move the common data structures to header file This patch moves the adv7511 data structure to header file so that the audio driver file could use it. Signed-off-by: Srinivas Kandagatla --- drivers/gpu/drm/i2c/adv7511.c | 50 ++-------------------------------------- drivers/gpu/drm/i2c/adv7511.h | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 46c1d7d427e7..6ae648ae0df6 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -24,52 +24,6 @@ #include "adv7511.h" -enum adv7511_type { - ADV7511, - ADV7533, -}; - -struct adv7511 { - struct i2c_client *i2c_main; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_cec; - - 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 current_edid_segment; - uint8_t edid_buf[256]; - bool edid_read; - - wait_queue_head_t wq; - struct drm_encoder *encoder; - - struct drm_connector connector; - struct drm_bridge bridge; - - 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; - - /* ADV7533 DSI RX related params */ - struct device_node *host_node; - struct mipi_dsi_device *dsi; - u8 num_dsi_lanes; - - enum adv7511_type type; -}; - static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) { return to_encoder_slave(encoder)->slave_priv; @@ -241,7 +195,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, @@ -256,7 +210,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, diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h index 6599ed538426..cbd57f34881e 100644 --- a/drivers/gpu/drm/i2c/adv7511.h +++ b/drivers/gpu/drm/i2c/adv7511.h @@ -10,6 +10,13 @@ #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); #define ADV7511_REG_CHIP_REVISION 0x00 #define ADV7511_REG_N0 0x01 @@ -229,6 +236,52 @@ 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; + + 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 current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + + wait_queue_head_t wq; + struct drm_encoder *encoder; + + struct drm_connector connector; + struct drm_bridge bridge; + + 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; + + /* ADV7533 DSI RX related params */ + struct device_node *host_node; + struct mipi_dsi_device *dsi; + u8 num_dsi_lanes; + + 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) -- cgit v1.2.3 From 7fb583f55a048369edd5d6f515bbceee8523268b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 17 Apr 2015 13:33:24 +0100 Subject: drm/i2c: adv7511: Add Audio support. This patch adds support to Audio for both adv7511 and adv7533 bridge chips. Content of this patch is has copied from [1]. [1] https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/gpu/drm/i2c/adv7511_audio.c Signed-off-by: Srinivas Kandagatla [Srinivas Kandagatla] Adapted to new adv7511 bridge driver. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/Makefile | 2 +- drivers/gpu/drm/i2c/adv7511.c | 3 + drivers/gpu/drm/i2c/adv7511.h | 5 + drivers/gpu/drm/i2c/adv7511_audio.c | 311 ++++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i2c/adv7511_audio.c diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 2c72eb584ab7..46cc9fb18367 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -1,6 +1,6 @@ ccflags-y := -Iinclude/drm -obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o +obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o adv7511_audio.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 index 6ae648ae0df6..bbcda466184b 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -1345,6 +1345,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } } + adv7511_audio_init(dev); + return 0; err_i2c_unregister_cec: @@ -1359,6 +1361,7 @@ 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); diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h index cbd57f34881e..e9008bf69caa 100644 --- a/drivers/gpu/drm/i2c/adv7511.h +++ b/drivers/gpu/drm/i2c/adv7511.h @@ -18,6 +18,9 @@ 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 #define ADV7511_REG_N1 0x02 @@ -254,6 +257,8 @@ struct adv7511 { 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]; diff --git a/drivers/gpu/drm/i2c/adv7511_audio.c b/drivers/gpu/drm/i2c/adv7511_audio.c new file mode 100644 index 000000000000..1f735470195d --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7511_audio.c @@ -0,0 +1,311 @@ +/* + * 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 +#include +#include +#include + +#include "adv7511.h" + +static const struct snd_soc_dapm_widget adv7511_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("TMDS"), + SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route adv7511_routes[] = { + { "TMDS", NULL, "AIFIN" }, +}; + +static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs, + unsigned int *cts, unsigned int *n) +{ + switch (fs) { + case 32000: + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + *n = 6144; + break; + } + + *cts = ((f_tmds * *n) / (128 * fs)) * 1000; +} + +static int adv7511_update_cts_n(struct adv7511 *adv7511) +{ + unsigned int cts = 0; + unsigned int n = 0; + + adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n); + + regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf); + regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff); + regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff); + + regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0, + (cts >> 16) & 0xf); + regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1, + (cts >> 8) & 0xff); + regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2, + cts & 0xff); + + return 0; +} + +static int adv7511_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct adv7511 *adv7511 = snd_soc_codec_get_drvdata(codec); + unsigned int rate; + unsigned int len; + switch (params_rate(params)) { + case 32000: + rate = ADV7511_SAMPLE_FREQ_32000; + break; + case 44100: + rate = ADV7511_SAMPLE_FREQ_44100; + break; + case 48000: + rate = ADV7511_SAMPLE_FREQ_48000; + break; + case 88200: + rate = ADV7511_SAMPLE_FREQ_88200; + break; + case 96000: + rate = ADV7511_SAMPLE_FREQ_96000; + break; + case 176400: + rate = ADV7511_SAMPLE_FREQ_176400; + break; + case 192000: + rate = ADV7511_SAMPLE_FREQ_192000; + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + len = ADV7511_I2S_SAMPLE_LEN_16; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + len = ADV7511_I2S_SAMPLE_LEN_18; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + len = ADV7511_I2S_SAMPLE_LEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + len = ADV7511_I2S_SAMPLE_LEN_24; + break; + default: + return -EINVAL; + } + + adv7511->f_audio = params_rate(params); + + adv7511_update_cts_n(adv7511); + + regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3, + ADV7511_AUDIO_CFG3_LEN_MASK, len); + regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, + ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4); + regmap_write(adv7511->regmap, 0x73, 0x1); + + return 0; +} + +static int adv7511_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct adv7511 *adv7511 = snd_soc_codec_get_drvdata(codec); + unsigned int audio_source, i2s_format = 0; + unsigned int invert_clock; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio_source = ADV7511_AUDIO_SOURCE_I2S; + i2s_format = ADV7511_I2S_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio_source = ADV7511_AUDIO_SOURCE_I2S; + i2s_format = ADV7511_I2S_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio_source = ADV7511_AUDIO_SOURCE_I2S; + i2s_format = ADV7511_I2S_FORMAT_LEFT_J; + break; +// case SND_SOC_DAIFMT_SPDIF: +// audio_source = ADV7511_AUDIO_SOURCE_SPDIF; +// break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_clock = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_clock = 1; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70, + audio_source << 4); + regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6), + invert_clock << 6); + regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03, + i2s_format); + + adv7511->audio_source = audio_source; + + return 0; +} + +static int adv7511_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct adv7511 *adv7511 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + switch (adv7511->audio_source) { + case ADV7511_AUDIO_SOURCE_I2S: + break; + case ADV7511_AUDIO_SOURCE_SPDIF: + regmap_update_bits(adv7511->regmap, + ADV7511_REG_AUDIO_CONFIG, BIT(7), + BIT(7)); + break; + } + break; + case SND_SOC_BIAS_PREPARE: + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + adv7511_packet_enable(adv7511, + ADV7511_PACKET_ENABLE_AUDIO_SAMPLE); + adv7511_packet_enable(adv7511, + ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME); + adv7511_packet_enable(adv7511, + ADV7511_PACKET_ENABLE_N_CTS); + } else { + adv7511_packet_disable(adv7511, + ADV7511_PACKET_ENABLE_AUDIO_SAMPLE); + adv7511_packet_disable(adv7511, + ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME); + adv7511_packet_disable(adv7511, + ADV7511_PACKET_ENABLE_N_CTS); + } + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, + BIT(7), 0); + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define ADV7511_RATES (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define ADV7511_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops adv7511_dai_ops = { + .hw_params = adv7511_hw_params, + /*.set_sysclk = adv7511_set_dai_sysclk,*/ + .set_fmt = adv7511_set_dai_fmt, +}; + +static struct snd_soc_dai_driver adv7511_dai = { + .name = "adv7511", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ADV7511_RATES, + .formats = ADV7511_FORMATS, + }, + .ops = &adv7511_dai_ops, +}; + +static int adv7511_suspend(struct snd_soc_codec *codec) +{ + return adv7511_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static int adv7511_resume(struct snd_soc_codec *codec) +{ + return adv7511_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +} + +static int adv7511_probe(struct snd_soc_codec *codec) +{ + return adv7511_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +} + +static int adv7511_remove(struct snd_soc_codec *codec) +{ + adv7511_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver adv7511_codec_driver = { + .probe = adv7511_probe, + .remove = adv7511_remove, + .suspend = adv7511_suspend, + .resume = adv7511_resume, + .set_bias_level = adv7511_set_bias_level, + + .dapm_widgets = adv7511_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adv7511_dapm_widgets), + .dapm_routes = adv7511_routes, + .num_dapm_routes = ARRAY_SIZE(adv7511_routes), +}; + +int adv7511_audio_init(struct device *dev) +{ + return snd_soc_register_codec(dev, &adv7511_codec_driver, + &adv7511_dai, 1); +} + +void adv7511_audio_exit(struct device *dev) +{ + snd_soc_unregister_codec(dev); +} -- cgit v1.2.3 From d08d80511aa5eda0e2a60ac5cd252c4e4e6e3a4a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 17 Apr 2015 13:25:01 +0100 Subject: drm/i2c: adv7511: Enable the audio data and clock pads on adv7533 This patch enables the Audio Data and Clock pads to the adv7533 bridge. Without this patch audio can not be played. Signed-off-by: Srinivas Kandagatla --- 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 bbcda466184b..6f2c32c2be4f 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -57,6 +57,7 @@ static const struct reg_default adv7533_cec_fixed_registers[] = { { 0x17, 0xd0 }, { 0x24, 0x20 }, { 0x57, 0x11 }, + { 0x05, 0xc8 }, }; /* ----------------------------------------------------------------------------- -- cgit v1.2.3 From 3fd9af510abdff3fcfc0ad87d4ae528df438cf46 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 7 Sep 2015 11:45:43 +0530 Subject: HACK: drm/i2c: adv7511: Remove hotplug event handling Hotplug detect is currently unstable. Temporarily remove the driver's hotplug handling code. Signed-off-by: Archit Taneja --- drivers/gpu/drm/i2c/adv7511.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 6f2c32c2be4f..7dddb9c159e9 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -24,6 +24,8 @@ #include "adv7511.h" +#define HPD_ENABLE 0 + static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) { return to_encoder_slave(encoder)->slave_priv; @@ -482,6 +484,7 @@ static void adv7511_power_off(struct adv7511 *adv7511) * Interrupt and hotplug detection */ +#if HPD_ENABLE static bool adv7511_hpd(struct adv7511 *adv7511) { unsigned int irq0; @@ -499,6 +502,7 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } +#endif static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { @@ -681,7 +685,9 @@ adv7511_detect(struct adv7511 *adv7511, { enum drm_connector_status status; unsigned int val; +#if HPD_ENABLE bool hpd; +#endif int ret; ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); @@ -693,6 +699,7 @@ adv7511_detect(struct adv7511 *adv7511, else status = connector_status_disconnected; +#if HPD_ENABLE hpd = adv7511_hpd(adv7511); /* The chip resets itself when the cable is disconnected, so in case @@ -711,6 +718,7 @@ adv7511_detect(struct adv7511 *adv7511, ADV7511_REG_POWER2_HDP_SRC_MASK, ADV7511_REG_POWER2_HDP_SRC_BOTH); } +#endif adv7511->status = status; return status; @@ -972,6 +980,11 @@ static void adv7533_bridge_post_disable(struct drm_bridge *bridge) { struct adv7511 *adv = bridge_to_adv7511(bridge); +#if HPD_ENABLE + if (!adv7511->powered) + return; +#endif + adv7511_power_off(adv); } @@ -1046,7 +1059,10 @@ static int adv7533_bridge_attach(struct drm_bridge *bridge) return -ENODEV; } +#if HPD_ENABLE adv->connector.polled = DRM_CONNECTOR_POLL_HPD; +#endif + ret = drm_connector_init(bridge->dev, &adv->connector, &adv7533_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); if (ret) { @@ -1058,7 +1074,9 @@ static int adv7533_bridge_attach(struct drm_bridge *bridge) drm_connector_register(&adv->connector); drm_mode_connector_attach_encoder(&adv->connector, adv->encoder); +#if HPD_ENABLE drm_helper_hpd_irq_event(adv->connector.dev); +#endif adv7533_attach_dsi(adv); -- cgit v1.2.3 From 7593d397f42430d4e0dfdf8361844b48cc53faec Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 16 Sep 2015 13:02:43 +0100 Subject: drm/i2c: adv7511: move to use reg_sequence Signed-off-by: Srinivas Kandagatla --- drivers/gpu/drm/i2c/adv7511.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 7dddb9c159e9..7298f794ae20 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -45,7 +45,7 @@ static const struct reg_sequence adv7511_fixed_registers[] = { }; /* ADI recommended values for proper operation. */ -static const struct reg_default adv7533_fixed_registers[] = { +static const struct reg_sequence adv7533_fixed_registers[] = { { 0x16, 0x20 }, { 0x9a, 0xe0 }, { 0xba, 0x70 }, @@ -54,7 +54,7 @@ static const struct reg_default adv7533_fixed_registers[] = { { 0xe5, 0x80 }, }; -static const struct reg_default adv7533_cec_fixed_registers[] = { +static const struct reg_sequence adv7533_cec_fixed_registers[] = { { 0x15, 0xd0 }, { 0x17, 0xd0 }, { 0x24, 0x20 }, -- cgit v1.2.3 From 6825b401ecb22bff0c2920b96622cef907a3d89c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 16 Sep 2015 13:03:39 +0100 Subject: drm/i2c: adv7511: fixup the dapm bias level access Signed-off-by: Srinivas Kandagatla --- drivers/gpu/drm/i2c/adv7511_audio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i2c/adv7511_audio.c b/drivers/gpu/drm/i2c/adv7511_audio.c index 1f735470195d..52019e95d007 100644 --- a/drivers/gpu/drm/i2c/adv7511_audio.c +++ b/drivers/gpu/drm/i2c/adv7511_audio.c @@ -198,6 +198,7 @@ static int adv7511_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct adv7511 *adv7511 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (level) { case SND_SOC_BIAS_ON: @@ -212,7 +213,7 @@ static int adv7511_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_SAMPLE); adv7511_packet_enable(adv7511, @@ -235,7 +236,7 @@ static int adv7511_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: break; } - codec->dapm.bias_level = level; + dapm->bias_level = level; return 0; } -- cgit v1.2.3