diff options
author | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2015-10-16 14:08:35 +0100 |
---|---|---|
committer | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2015-10-16 14:08:35 +0100 |
commit | 31de303a54fd1c78655790ce1ddf8d92b9eb441e (patch) | |
tree | 57b25f76eedbeb1f55dfda774e28d456d6c3703c | |
parent | 6a3f6cd57145363ba4e739ecdfc227431797c302 (diff) | |
parent | b595b18b0d0bda973c3cb616d08a94bb079ee29d (diff) |
Merge branch 'tracking-qcomlt-adv7511' into integration-linux-qcomlt
* tracking-qcomlt-adv7511:
drm/i2c: adv7511: fixup the dapm bias level access
drm/i2c: adv7511: move to use reg_sequence
HACK: drm/i2c: adv7511: Remove hotplug event handling
drm/i2c: adv7511: Enable the audio data and clock pads on adv7533
drm/i2c: adv7511: Add Audio support.
drm/i2c: adv7511: Move the common data structures to header file
drm/i2c: adv7511: Change DSI lanes dynamically
drm/i2c: adv7511: Use internal timing generator
drm/i2c: adv7511: setup CEC registers during power off-power-on sequence
drm/i2c: adv7511: Create mipi_dsi_device for ADV7533
drm/i2c: adv7511: Add drm_bridge/connector for ADV7533
drm/i2c: adv7511: Refactor encoder slave functions
drm/i2c: adv7511: Initial support for adv7533
drm/i2c: adv7511: Fix mutex deadlock when interrupts are disabled
drm/mipi_dsi: Get DSI host by DT device node
drm/mipi_dsi: Create dummy DSI devices
drm/mipi_dsi: check for used channels
drm/mipi_dsi: refactor device creation
-rw-r--r-- | drivers/gpu/drm/drm_mipi_dsi.c | 161 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/adv7511.c | 592 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/adv7511.h | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/adv7511_audio.c | 312 | ||||
-rw-r--r-- | include/drm/drm_mipi_dsi.h | 9 |
6 files changed, 1021 insertions, 113 deletions
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 2d5ca8eec13a..e487f56ffc4c 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 = { @@ -102,9 +119,41 @@ 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 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) { + 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,62 +163,90 @@ 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; + r = mipi_dsi_check_chan_busy(host, info->reg); + if (r) + goto err; - dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), dsi->channel); + r = device_register(&dsi->dev); + if (r) + goto err; - return device_add(&dsi->dev); + return dsi; +err: + kfree(dsi); + return ERR_PTR(r); } 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 (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); + } + + ret = of_property_read_u32(node, "reg", &info.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); - return ERR_PTR(-EINVAL); - } + info.node = of_node_get(node); - 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; - } + return mipi_dsi_device_new(host, &info); +} + +static struct mipi_dsi_driver dummy_dsi_driver = { + .driver.name = "mipi_dsi_dummy", +}; - dsi->dev.of_node = of_node_get(node); - dsi->channel = reg; +struct mipi_dsi_device *mipi_dsi_new_dummy(struct mipi_dsi_host *host, u32 reg) +{ + struct mipi_dsi_device_info info = { "dummy", reg, NULL, }; - ret = mipi_dsi_device_add(dsi); - if (ret) { - dev_err(dev, "failed to add DSI device %s: %d\n", - node->full_name, ret); - kfree(dsi); - return ERR_PTR(ret); + 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); + +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; + } } - return dsi; + 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) { @@ -182,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); @@ -198,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); @@ -924,7 +1009,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/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 00416f23b5cb..7298f794ae20 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -12,41 +12,19 @@ #include <linux/module.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/of_graph.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_mipi_dsi.h> #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; -}; +#define HPD_ENABLE 0 static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) { @@ -66,6 +44,24 @@ static const struct reg_sequence adv7511_fixed_registers[] = { { 0x55, 0x02 }, }; +/* ADI recommended values for proper operation. */ +static const struct reg_sequence adv7533_fixed_registers[] = { + { 0x16, 0x20 }, + { 0x9a, 0xe0 }, + { 0xba, 0x70 }, + { 0xde, 0x82 }, + { 0xe4, 0x40 }, + { 0xe5, 0x80 }, +}; + +static const struct reg_sequence adv7533_cec_fixed_registers[] = { + { 0x15, 0xd0 }, + { 0x17, 0xd0 }, + { 0x24, 0x20 }, + { 0x57, 0x11 }, + { 0x05, 0xc8 }, +}; + /* ----------------------------------------------------------------------------- * Register access */ @@ -158,6 +154,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 +198,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 +213,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, @@ -358,6 +363,73 @@ 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) + return; + + 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); + + /* 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); + } +} + static void adv7511_power_on(struct adv7511 *adv7511) { adv7511->current_edid_segment = -1; @@ -386,7 +458,13 @@ 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); } static void adv7511_power_off(struct adv7511 *adv7511) @@ -398,12 +476,15 @@ static void adv7511_power_off(struct adv7511 *adv7511) regcache_mark_dirty(adv7511->regmap); adv7511->powered = false; + + adv7511_dsi_receiver_dpms(adv7511); } /* ----------------------------------------------------------------------------- * Interrupt and hotplug detection */ +#if HPD_ENABLE static bool adv7511_hpd(struct adv7511 *adv7511) { unsigned int irq0; @@ -421,8 +502,9 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } +#endif -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 +520,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 +538,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 +555,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; @@ -555,18 +637,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,24 +679,15 @@ 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; +#if HPD_ENABLE bool hpd; +#endif int ret; ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); @@ -625,6 +699,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder, else status = connector_status_disconnected; +#if HPD_ENABLE hpd = adv7511_hpd(adv7511); /* The chip resets itself when the cable is disconnected, so in case @@ -634,7 +709,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 { @@ -643,13 +718,14 @@ adv7511_encoder_detect(struct drm_encoder *encoder, ADV7511_REG_POWER2_HDP_SRC_MASK, ADV7511_REG_POWER2_HDP_SRC_BOTH); } +#endif adv7511->status = status; 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; @@ -657,11 +733,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; @@ -744,6 +819,28 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, 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); + /* * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is * supposed to give better results. @@ -752,12 +849,247 @@ 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, +}; + +/* ----------------------------------------------------------------------------- + * 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); + +#if HPD_ENABLE + if (!adv7511->powered) + return; +#endif + + 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_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); + int ret; + + adv->encoder = bridge->encoder; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + 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) { + 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); + +#if HPD_ENABLE + drm_helper_hpd_irq_event(adv->connector.dev); +#endif + + adv7533_attach_dsi(adv); + + 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, }; /* ----------------------------------------------------------------------------- @@ -770,8 +1102,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) @@ -849,10 +1179,54 @@ 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; +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,7 +1245,21 @@ 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 = (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); + else + ret = adv7533_parse_dt(dev->of_node, adv7511); if (ret) return ret; @@ -897,10 +1285,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 +1310,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 +1339,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 +1350,27 @@ 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); + + 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; + } + } + + adv7511_audio_init(dev); 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,10 +1380,18 @@ 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); + if (adv7511->type == ADV7533) { + mipi_dsi_detach(adv7511->dsi); + mipi_dsi_unregister_device(adv7511->dsi); + drm_bridge_remove(&adv7511->bridge); + } + return 0; } @@ -959,6 +1401,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; @@ -968,21 +1413,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 = { diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h index 6599ed538426..e9008bf69caa 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 <linux/hdmi.h> +#include <drm/drm_crtc_helper.h> + +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; + + 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; + + 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) diff --git a/drivers/gpu/drm/i2c/adv7511_audio.c b/drivers/gpu/drm/i2c/adv7511_audio.c new file mode 100644 index 000000000000..52019e95d007 --- /dev/null +++ b/drivers/gpu/drm/i2c/adv7511_audio.c @@ -0,0 +1,312 @@ +/* + * Analog Devices ADV7511 HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#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); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(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 (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; + } + 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); +} diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index f1d8d0dbb4f1..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 */ @@ -139,10 +141,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 +157,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; @@ -163,6 +170,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); |