aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-10-16 14:08:35 +0100
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-10-16 14:08:35 +0100
commit31de303a54fd1c78655790ce1ddf8d92b9eb441e (patch)
tree57b25f76eedbeb1f55dfda774e28d456d6c3703c
parent6a3f6cd57145363ba4e739ecdfc227431797c302 (diff)
parentb595b18b0d0bda973c3cb616d08a94bb079ee29d (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.c161
-rw-r--r--drivers/gpu/drm/i2c/Makefile2
-rw-r--r--drivers/gpu/drm/i2c/adv7511.c592
-rw-r--r--drivers/gpu/drm/i2c/adv7511.h58
-rw-r--r--drivers/gpu/drm/i2c/adv7511_audio.c312
-rw-r--r--include/drm/drm_mipi_dsi.h9
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, &reg, __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", &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);