summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/msm/Kconfig1
-rw-r--r--drivers/video/fbdev/msm/Makefile1
-rw-r--r--drivers/video/fbdev/msm/msm_dba/Kconfig15
-rw-r--r--drivers/video/fbdev/msm/msm_dba/Makefile3
-rw-r--r--drivers/video/fbdev/msm/msm_dba/msm_dba.c146
-rw-r--r--drivers/video/fbdev/msm/msm_dba/msm_dba_debug.c299
-rw-r--r--drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c445
-rw-r--r--drivers/video/fbdev/msm/msm_dba/msm_dba_init.c131
-rw-r--r--drivers/video/fbdev/msm/msm_dba/msm_dba_internal.h317
-rw-r--r--include/video/msm_dba.h503
10 files changed, 1861 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig
index a4d5d5e0d637..c49ce06430be 100644
--- a/drivers/video/fbdev/msm/Kconfig
+++ b/drivers/video/fbdev/msm/Kconfig
@@ -1,3 +1,4 @@
+source "drivers/video/fbdev/msm/msm_dba/Kconfig"
if FB_MSM
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index eadf8c7191ee..ac83251cbfe6 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -1,6 +1,7 @@
ccflags-y += -I$(src) -Idrivers/staging/android
obj-$(CONFIG_FB_MSM_MDSS_MHL3) += mhl3/
+obj-$(CONFIG_MSM_DBA) += msm_dba/
mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o
mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o
diff --git a/drivers/video/fbdev/msm/msm_dba/Kconfig b/drivers/video/fbdev/msm/msm_dba/Kconfig
new file mode 100644
index 000000000000..a20d59c16a0d
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/Kconfig
@@ -0,0 +1,15 @@
+#
+# MSM DBA
+#
+
+config MSM_DBA
+ bool "MSM Display Bridge Abstraction support"
+ depends on ARM
+ ---help---
+ Support for MSM display bridge abstraction interface. MSM display
+ drivers can use the same interface to interact with different third
+ party bridge chips. Drivers implemented for third party bridge chips
+ should support this interface to allow display driver to control the
+ bridge chip. The MSM DBA driver maintains a list of devices supported
+ on the platform and allow clients to register and access these
+ devices.
diff --git a/drivers/video/fbdev/msm/msm_dba/Makefile b/drivers/video/fbdev/msm/msm_dba/Makefile
new file mode 100644
index 000000000000..68a1adf3f88e
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MSM_DBA) += msm_dba.o msm_dba_init.o msm_dba_helpers.o msm_dba_debug.o
+clean:
+ rm *.o
diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba.c b/drivers/video/fbdev/msm/msm_dba/msm_dba.c
new file mode 100644
index 000000000000..7a5c9d9d873a
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/msm_dba.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <video/msm_dba.h>
+#include <msm_dba_internal.h>
+
+static DEFINE_MUTEX(register_mutex);
+
+void *msm_dba_register_client(struct msm_dba_reg_info *info,
+ struct msm_dba_ops *ops)
+{
+ int rc = 0;
+ struct msm_dba_device_info *device = NULL;
+ struct msm_dba_client_info *client = NULL;
+
+ pr_debug("%s: ENTER\n", __func__);
+
+ if (!info || !ops) {
+ pr_err("%s: Invalid params\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mutex_lock(&register_mutex);
+
+ pr_debug("%s: Client(%s) Chip(%s) Instance(%d)\n", __func__,
+ info->client_name, info->chip_name, info->instance_id);
+
+ rc = msm_dba_get_probed_device(info, &device);
+ if (rc) {
+ pr_err("%s: Device not found (%s, %d)\n", __func__,
+ info->chip_name,
+ info->instance_id);
+ mutex_unlock(&register_mutex);
+ return ERR_PTR(rc);
+ }
+
+ pr_debug("%s: Client(%s) device found\n", __func__, info->client_name);
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client) {
+ mutex_unlock(&register_mutex);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memset(client, 0x0, sizeof(*client));
+ client->dev = device;
+ strlcpy(client->client_name, info->client_name,
+ MSM_DBA_CLIENT_NAME_LEN);
+
+ client->cb = info->cb;
+ client->cb_data = info->cb_data;
+
+ mutex_lock_nested(&device->dev_mutex, SINGLE_DEPTH_NESTING);
+ list_add(&client->list, &device->client_list);
+ *ops = device->client_ops;
+ mutex_unlock(&device->dev_mutex);
+
+ if (device->reg_fxn) {
+ rc = device->reg_fxn(client);
+ if (rc) {
+ pr_err("%s: Client register failed (%s, %d)\n",
+ __func__, info->chip_name, info->instance_id);
+ kfree(client);
+ mutex_unlock(&register_mutex);
+ return ERR_PTR(rc);
+ }
+ }
+
+ mutex_unlock(&register_mutex);
+
+ pr_debug("%s: EXIT\n", __func__);
+ return client;
+}
+EXPORT_SYMBOL(msm_dba_register_client);
+
+int msm_dba_deregister_client(void *client)
+{
+ int rc = 0;
+ struct msm_dba_client_info *handle = client;
+ struct msm_dba_client_info *node = NULL;
+ struct list_head *tmp = NULL;
+ struct list_head *position = NULL;
+
+ pr_debug("%s: ENTER\n", __func__);
+
+ if (!handle) {
+ pr_err("%s: Invalid Params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&register_mutex);
+
+ pr_debug("%s: Client(%s) Chip(%s) Instance(%d)\n", __func__,
+ handle->client_name, handle->dev->chip_name,
+ handle->dev->instance_id);
+
+ if (handle->dev->dereg_fxn) {
+ rc = handle->dev->dereg_fxn(handle);
+ if (rc) {
+ pr_err("%s: Client deregister failed (%s)\n",
+ __func__, handle->client_name);
+ }
+ }
+
+ mutex_lock_nested(&handle->dev->dev_mutex, SINGLE_DEPTH_NESTING);
+
+ list_for_each_safe(position, tmp, &handle->dev->client_list) {
+
+ node = list_entry(position, struct msm_dba_client_info, list);
+
+ if (node == handle) {
+ list_del(&node->list);
+ break;
+ }
+ }
+
+ mutex_unlock(&handle->dev->dev_mutex);
+
+ kfree(handle);
+
+ mutex_unlock(&register_mutex);
+
+ pr_debug("%s: EXIT (%d)\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL(msm_dba_deregister_client);
diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba_debug.c b/drivers/video/fbdev/msm/msm_dba/msm_dba_debug.c
new file mode 100644
index 000000000000..b59dd583b98d
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/msm_dba_debug.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+
+#include <video/msm_dba.h>
+#include "msm_dba_internal.h"
+
+static inline struct msm_dba_device_info *to_dba_dev(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+static ssize_t device_name_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s:%d\n", device->chip_name,
+ device->instance_id);
+}
+
+static ssize_t client_list_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ struct msm_dba_client_info *c;
+ struct list_head *pos = NULL;
+ ssize_t bytes = 0;
+
+ mutex_lock(&device->dev_mutex);
+
+ list_for_each(pos, &device->client_list) {
+ c = list_entry(pos, struct msm_dba_client_info, list);
+ bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes), "%s\n",
+ c->client_name);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+
+ return bytes;
+}
+
+static ssize_t power_status_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ struct msm_dba_client_info *c;
+ struct list_head *pos = NULL;
+ ssize_t bytes = 0;
+
+ mutex_lock(&device->dev_mutex);
+ bytes = snprintf(buf, PAGE_SIZE, "power_status:%d\n",
+ device->power_status);
+
+ list_for_each(pos, &device->client_list) {
+ c = list_entry(pos, struct msm_dba_client_info, list);
+ bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
+ "client: %s, status = %d\n",
+ c->client_name, c->power_on);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ return bytes;
+}
+
+static ssize_t video_status_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ struct msm_dba_client_info *c;
+ struct list_head *pos = NULL;
+ ssize_t bytes = 0;
+
+ mutex_lock(&device->dev_mutex);
+ bytes = snprintf(buf, PAGE_SIZE, "video_status:%d\n",
+ device->video_status);
+
+ list_for_each(pos, &device->client_list) {
+ c = list_entry(pos, struct msm_dba_client_info, list);
+ bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
+ "client: %s, status = %d\n",
+ c->client_name, c->video_on);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ return bytes;
+}
+
+static ssize_t audio_status_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ struct msm_dba_client_info *c;
+ struct list_head *pos = NULL;
+ ssize_t bytes = 0;
+
+ mutex_lock(&device->dev_mutex);
+ bytes = snprintf(buf, PAGE_SIZE, "audio_status:%d\n",
+ device->audio_status);
+
+ list_for_each(pos, &device->client_list) {
+ c = list_entry(pos, struct msm_dba_client_info, list);
+ bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
+ "client: %s, status = %d\n",
+ c->client_name, c->audio_on);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ return bytes;
+}
+
+static ssize_t write_reg_wta_attr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ char *regstr, *valstr, *ptr;
+ char str[20];
+ long reg = 0;
+ long val = 0;
+ int rc = 0;
+ int len;
+
+ len = strlen(buf);
+ strlcpy(str, buf, 20);
+ if (len < 20)
+ str[len] = '\0';
+ else
+ str[19] = '\0';
+
+ ptr = str;
+ regstr = strsep(&ptr, ":");
+ valstr = strsep(&ptr, ":");
+
+ rc = kstrtol(regstr, 0, &reg);
+ if (rc) {
+ pr_err("%s: kstrol error %d\n", __func__, rc);
+ } else {
+ rc = kstrtol(valstr, 0, &val);
+ if (rc)
+ pr_err("%s: kstrol error for val %d\n", __func__, rc);
+ }
+
+ if (!rc) {
+ mutex_lock(&device->dev_mutex);
+
+ if (device->dev_ops.write_reg) {
+ rc = device->dev_ops.write_reg(device,
+ (u32)reg,
+ (u32)val);
+
+ if (rc) {
+ pr_err("%s: failed to write reg %d", __func__,
+ rc);
+ }
+ } else {
+ pr_err("%s: not supported\n", __func__);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ }
+
+ return count;
+}
+
+static ssize_t read_reg_rda_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ ssize_t bytes;
+
+ mutex_lock(&device->dev_mutex);
+
+ bytes = snprintf(buf, PAGE_SIZE, "0x%x\n", device->register_val);
+
+ mutex_unlock(&device->dev_mutex);
+
+ return bytes;
+}
+
+static ssize_t read_reg_wta_attr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ long reg = 0;
+ int rc = 0;
+ u32 val = 0;
+
+ rc = kstrtol(buf, 0, &reg);
+ if (rc) {
+ pr_err("%s: kstrol error %d\n", __func__, rc);
+ } else {
+ mutex_lock(&device->dev_mutex);
+
+ if (device->dev_ops.read_reg) {
+ rc = device->dev_ops.read_reg(device,
+ (u32)reg,
+ &val);
+
+ if (rc) {
+ pr_err("%s: failed to write reg %d", __func__,
+ rc);
+ } else {
+ device->register_val = val;
+ }
+ } else {
+ pr_err("%s: not supported\n", __func__);
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ }
+
+ return count;
+}
+
+static ssize_t dump_info_wta_attr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct msm_dba_device_info *device = to_dba_dev(dev);
+ int rc;
+
+ rc = device->dev_ops.dump_debug_info(device, 0x00);
+ if (rc)
+ pr_err("%s: failed to dump debug data\n", __func__);
+
+ return count;
+}
+
+static DEVICE_ATTR(device_name, S_IRUGO, device_name_rda_attr, NULL);
+static DEVICE_ATTR(client_list, S_IRUGO, client_list_rda_attr, NULL);
+static DEVICE_ATTR(power_status, S_IRUGO, power_status_rda_attr, NULL);
+static DEVICE_ATTR(video_status, S_IRUGO, video_status_rda_attr, NULL);
+static DEVICE_ATTR(audio_status, S_IRUGO, audio_status_rda_attr, NULL);
+static DEVICE_ATTR(write_reg, S_IWUSR, NULL, write_reg_wta_attr);
+static DEVICE_ATTR(read_reg, S_IRUGO | S_IWUSR, read_reg_rda_attr,
+ read_reg_wta_attr);
+static DEVICE_ATTR(dump_info, S_IWUSR, NULL, dump_info_wta_attr);
+
+static struct attribute *msm_dba_sysfs_attrs[] = {
+ &dev_attr_device_name.attr,
+ &dev_attr_client_list.attr,
+ &dev_attr_power_status.attr,
+ &dev_attr_video_status.attr,
+ &dev_attr_audio_status.attr,
+ &dev_attr_write_reg.attr,
+ &dev_attr_read_reg.attr,
+ &dev_attr_dump_info.attr,
+ NULL,
+};
+
+static struct attribute_group msm_dba_sysfs_attr_grp = {
+ .attrs = msm_dba_sysfs_attrs,
+};
+
+int msm_dba_helper_sysfs_init(struct device *dev)
+{
+ int rc = 0;
+
+ if (!dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = sysfs_create_group(&dev->kobj, &msm_dba_sysfs_attr_grp);
+ if (rc)
+ pr_err("%s: sysfs group creation failed %d\n", __func__, rc);
+
+ return rc;
+}
diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c b/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c
new file mode 100644
index 000000000000..f6128ae01a75
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <video/msm_dba.h>
+#include "msm_dba_internal.h"
+
+static void msm_dba_helper_hdcp_handler(struct work_struct *work)
+{
+ struct msm_dba_device_info *dev;
+ int rc = 0;
+
+ if (!work) {
+ pr_err("%s: Invalid params\n", __func__);
+ return;
+ }
+
+ dev = container_of(work, struct msm_dba_device_info, hdcp_work);
+
+ mutex_lock(&dev->dev_mutex);
+ if (dev->hdcp_status) {
+ pr_debug("%s[%s:%d] HDCP is authenticated\n", __func__,
+ dev->chip_name, dev->instance_id);
+ mutex_unlock(&dev->dev_mutex);
+ return;
+ }
+
+ if (dev->dev_ops.hdcp_reset) {
+ rc = dev->dev_ops.hdcp_reset(dev);
+ if (rc)
+ pr_err("%s[%s:%d] HDCP reset failed\n", __func__,
+ dev->chip_name, dev->instance_id);
+ }
+
+ if (dev->dev_ops.hdcp_retry) {
+ rc = dev->dev_ops.hdcp_retry(dev, MSM_DBA_ASYNC_FLAG);
+ if (rc)
+ pr_err("%s[%s:%d] HDCP retry failed\n", __func__,
+ dev->chip_name, dev->instance_id);
+ }
+ mutex_unlock(&dev->dev_mutex);
+}
+
+static void msm_dba_helper_issue_cb(struct msm_dba_device_info *dev,
+ struct msm_dba_client_info *client,
+ enum msm_dba_callback_event event)
+{
+ struct msm_dba_client_info *c;
+ struct list_head *pos = NULL;
+ u32 user_mask = 0;
+
+ list_for_each(pos, &dev->client_list) {
+ c = list_entry(pos, struct msm_dba_client_info, list);
+ if (client && client == c)
+ continue;
+
+ user_mask = c->event_mask & event;
+ if (c->cb && user_mask)
+ c->cb(c->cb_data, user_mask);
+ }
+}
+
+static irqreturn_t msm_dba_helper_irq_handler(int irq, void *dev)
+{
+ struct msm_dba_device_info *device = dev;
+ u32 mask = 0;
+ int rc = 0;
+ bool ret;
+
+ mutex_lock(&device->dev_mutex);
+ if (device->dev_ops.handle_interrupts) {
+ rc = device->dev_ops.handle_interrupts(device, &mask);
+ if (rc)
+ pr_err("%s: interrupt handler failed\n", __func__);
+ }
+
+ pr_debug("%s(%s:%d): Eventmask = 0x%x\n", __func__, device->chip_name,
+ device->instance_id, mask);
+ if (mask)
+ msm_dba_helper_issue_cb(device, NULL, mask);
+
+ if ((mask & MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED) &&
+ device->hdcp_monitor_on) {
+ ret = queue_work(device->hdcp_wq, &device->hdcp_work);
+ if (!ret)
+ pr_err("%s: queue_work failed %d\n", __func__, rc);
+ }
+
+ if (device->dev_ops.unmask_interrupts)
+ rc = device->dev_ops.unmask_interrupts(device, mask);
+
+ mutex_unlock(&device->dev_mutex);
+ return IRQ_HANDLED;
+}
+
+int msm_dba_helper_i2c_write_byte(struct i2c_client *client,
+ u8 addr,
+ u8 reg,
+ u8 val)
+{
+ int rc = 0;
+ struct i2c_msg msg;
+ u8 buf[2] = {reg, val};
+
+ if (!client) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: [%s:0x02%x] : W[0x%02x, 0x%02x]\n", __func__,
+ client->name, addr, reg, val);
+ client->addr = addr;
+
+ msg.addr = addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = buf;
+
+ if (i2c_transfer(client->adapter, &msg, 1) < 1) {
+ pr_err("%s: i2c write failed\n", __func__);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+int msm_dba_helper_i2c_write_buffer(struct i2c_client *client,
+ u8 addr,
+ u8 *buf,
+ u32 size)
+{
+ int rc = 0;
+ struct i2c_msg msg;
+
+ if (!client) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: [%s:0x02%x] : W %d bytes\n", __func__,
+ client->name, addr, size);
+
+ client->addr = addr;
+
+ msg.addr = addr;
+ msg.flags = 0;
+ msg.len = size;
+ msg.buf = buf;
+
+ if (i2c_transfer(client->adapter, &msg, 1) != 1) {
+ pr_err("%s: i2c write failed\n", __func__);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+int msm_dba_helper_i2c_read(struct i2c_client *client,
+ u8 addr,
+ u8 reg,
+ char *buf,
+ u32 size)
+{
+ int rc = 0;
+ struct i2c_msg msg[2];
+
+ if (!client || !buf) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ client->addr = addr;
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = size;
+ msg[1].buf = buf;
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ pr_err("%s: i2c read failed\n", __func__);
+ rc = -EIO;
+ }
+
+ pr_debug("%s: [%s:0x02%x] : R[0x%02x, 0x%02x]\n", __func__,
+ client->name, addr, reg, *buf);
+ return rc;
+}
+
+int msm_dba_helper_power_on(void *client, bool on, u32 flags)
+{
+ int rc = 0;
+ struct msm_dba_client_info *c = client;
+ struct msm_dba_device_info *device;
+ struct msm_dba_client_info *node;
+ struct list_head *pos = NULL;
+ bool power_on = false;
+
+ if (!c) {
+ pr_err("%s: Invalid Params\n", __func__);
+ return -EINVAL;
+ }
+
+ device = c->dev;
+ mutex_lock(&device->dev_mutex);
+
+ /*
+ * Power on the device if atleast one client powers on the device. But
+ * power off will be done only after all the clients have called power
+ * off
+ */
+ if (on == device->power_status) {
+ c->power_on = on;
+ } else if (on) {
+ rc = device->dev_ops.dev_power_on(device, on);
+ if (rc)
+ pr_err("%s:%s: power on failed\n", device->chip_name,
+ __func__);
+ else
+ c->power_on = on;
+ } else {
+ c->power_on = false;
+
+ list_for_each(pos, &device->client_list) {
+ node = list_entry(pos, struct msm_dba_client_info,
+ list);
+ if (c->power_on) {
+ power_on = true;
+ break;
+ }
+ }
+
+ if (!power_on) {
+ rc = device->dev_ops.dev_power_on(device, false);
+ if (rc) {
+ pr_err("%s:%s: power off failed\n",
+ device->chip_name, __func__);
+ c->power_on = true;
+ }
+ }
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ return rc;
+}
+
+int msm_dba_helper_video_on(void *client, bool on,
+ struct msm_dba_video_cfg *cfg, u32 flags)
+{
+ int rc = 0;
+ struct msm_dba_client_info *c = client;
+ struct msm_dba_device_info *device;
+ struct msm_dba_client_info *node;
+ struct list_head *pos = NULL;
+ bool video_on = false;
+
+ if (!c) {
+ pr_err("%s: Invalid Params\n", __func__);
+ return -EINVAL;
+ }
+
+ device = c->dev;
+ mutex_lock(&device->dev_mutex);
+
+ /*
+ * Video will be turned on if at least one client turns on video. But
+ * video off will be done only after all the clients have called video
+ * off
+ */
+ if (on == device->video_status) {
+ c->video_on = on;
+ } else if (on) {
+ rc = device->dev_ops.dev_video_on(device, cfg, on);
+ if (rc)
+ pr_err("%s:%s: video on failed\n", device->chip_name,
+ __func__);
+ else
+ c->video_on = on;
+ } else {
+ c->video_on = false;
+
+ list_for_each(pos, &device->client_list) {
+ node = list_entry(pos, struct msm_dba_client_info,
+ list);
+ if (c->video_on) {
+ video_on = true;
+ break;
+ }
+ }
+
+ if (!video_on) {
+ rc = device->dev_ops.dev_video_on(device, cfg, false);
+ if (rc) {
+ pr_err("%s:%s: video off failed\n",
+ device->chip_name, __func__);
+ c->video_on = true;
+ }
+ }
+ }
+
+ mutex_unlock(&device->dev_mutex);
+ return rc;
+}
+
+int msm_dba_helper_interrupts_enable(void *client, bool on,
+ u32 event_mask, u32 flags)
+{
+ struct msm_dba_client_info *c = client;
+ struct msm_dba_device_info *device;
+
+ if (!c) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ device = c->dev;
+ mutex_lock(&device->dev_mutex);
+
+ if (on)
+ c->event_mask = event_mask;
+ else
+ c->event_mask = 0;
+
+ mutex_unlock(&device->dev_mutex);
+ return 0;
+}
+
+int msm_dba_helper_register_irq(struct msm_dba_device_info *dev,
+ u32 irq, u32 irq_flags)
+{
+ int rc;
+
+ if (!dev) {
+ pr_err("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->dev_mutex);
+
+ rc = request_threaded_irq(irq, NULL, msm_dba_helper_irq_handler,
+ irq_flags, dev->chip_name, dev);
+
+ if (rc)
+ pr_err("%s:%s: Failed to register irq\n", dev->chip_name,
+ __func__);
+
+ mutex_unlock(&dev->dev_mutex);
+ return rc;
+}
+
+int msm_dba_helper_get_caps(void *client, struct msm_dba_capabilities *caps)
+{
+ struct msm_dba_client_info *c = client;
+ struct msm_dba_device_info *device;
+
+ if (!c || !caps) {
+ pr_err("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ device = c->dev;
+ mutex_lock(&device->dev_mutex);
+
+ memcpy(caps, &device->caps, sizeof(*caps));
+
+ mutex_unlock(&device->dev_mutex);
+ return 0;
+}
+
+int msm_dba_register_hdcp_monitor(struct msm_dba_device_info *dev, bool enable)
+{
+ int rc = 0;
+
+ if (!dev) {
+ pr_err("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ dev->hdcp_wq = alloc_workqueue("hdcp_monitor(%s:%d)", 0, 0,
+ dev->chip_name,
+ dev->instance_id);
+ if (!dev->hdcp_wq) {
+ pr_err("%s: failed to allocate wq\n", __func__);
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ INIT_WORK(&dev->hdcp_work, msm_dba_helper_hdcp_handler);
+ dev->hdcp_monitor_on = true;
+ } else if (!enable && dev->hdcp_wq) {
+ destroy_workqueue(dev->hdcp_wq);
+ dev->hdcp_wq = NULL;
+ dev->hdcp_monitor_on = false;
+ }
+
+fail:
+ return rc;
+}
+
+int msm_dba_helper_force_reset(void *client, u32 flags)
+{
+ struct msm_dba_client_info *c = client;
+ struct msm_dba_device_info *device;
+ int rc = 0;
+
+ if (!c) {
+ pr_err("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ device = c->dev;
+ mutex_lock(&device->dev_mutex);
+
+ msm_dba_helper_issue_cb(device, c, MSM_DBA_CB_PRE_RESET);
+
+ if (device->dev_ops.force_reset)
+ rc = device->dev_ops.force_reset(device, flags);
+
+ if (rc)
+ pr_err("%s: Force reset failed\n", __func__);
+
+ msm_dba_helper_issue_cb(device, c, MSM_DBA_CB_POST_RESET);
+
+ mutex_unlock(&device->dev_mutex);
+ return rc;
+}
diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba_init.c b/drivers/video/fbdev/msm/msm_dba/msm_dba_init.c
new file mode 100644
index 000000000000..96d0d70bccf8
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/msm_dba_init.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <video/msm_dba.h>
+#include "msm_dba_internal.h"
+
+struct msm_dba_device_list {
+ struct msm_dba_device_info *dev;
+ struct list_head list;
+};
+
+static LIST_HEAD(device_list);
+static DEFINE_MUTEX(init_mutex);
+
+int msm_dba_add_probed_device(struct msm_dba_device_info *dev)
+{
+ struct msm_dba_device_list *node;
+
+ if (!dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&init_mutex);
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ mutex_unlock(&init_mutex);
+ return -ENOMEM;
+ }
+
+ memset(node, 0x0, sizeof(*node));
+ node->dev = dev;
+ list_add(&node->list, &device_list);
+
+ pr_debug("%s: Added new device (%s, %d)", __func__, dev->chip_name,
+ dev->instance_id);
+
+ mutex_unlock(&init_mutex);
+
+ return 0;
+}
+
+int msm_dba_get_probed_device(struct msm_dba_reg_info *reg,
+ struct msm_dba_device_info **dev)
+{
+ int rc = 0;
+ struct msm_dba_device_list *node;
+ struct list_head *position = NULL;
+
+ if (!reg || !dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&init_mutex);
+
+ *dev = NULL;
+ list_for_each(position, &device_list) {
+ node = list_entry(position, struct msm_dba_device_list, list);
+ if (!strcmp(reg->chip_name, node->dev->chip_name) &&
+ reg->instance_id == node->dev->instance_id) {
+ pr_debug("%s: Found device (%s, %d)\n", __func__,
+ reg->chip_name,
+ reg->instance_id);
+ *dev = node->dev;
+ break;
+ }
+ }
+
+ if (!*dev) {
+ pr_err("%s: Device not found (%s, %d)\n", __func__,
+ reg->chip_name,
+ reg->instance_id);
+ rc = -ENODEV;
+ }
+
+ mutex_unlock(&init_mutex);
+
+ return rc;
+}
+
+int msm_dba_remove_probed_device(struct msm_dba_device_info *dev)
+{
+ struct msm_dba_device_list *node;
+ struct list_head *position = NULL;
+ struct list_head *temp = NULL;
+
+ if (!dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&init_mutex);
+
+ list_for_each_safe(position, temp, &device_list) {
+ node = list_entry(position, struct msm_dba_device_list, list);
+ if (node->dev == dev) {
+ list_del(&node->list);
+ pr_debug("%s: Removed device (%s, %d)\n", __func__,
+ dev->chip_name,
+ dev->instance_id);
+ kfree(node);
+ break;
+ }
+ }
+
+ mutex_unlock(&init_mutex);
+
+ return 0;
+}
diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba_internal.h b/drivers/video/fbdev/msm/msm_dba/msm_dba_internal.h
new file mode 100644
index 000000000000..5da673bfcffb
--- /dev/null
+++ b/drivers/video/fbdev/msm/msm_dba/msm_dba_internal.h
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DBA_INTERNAL_H
+#define _MSM_DBA_INTERNAL_H
+
+#include <video/msm_dba.h>
+
+struct msm_dba_client_info;
+struct msm_dba_device_info;
+
+/**
+ * struct msm_dba_device_ops - Function pointers to device specific operations
+ * @dev_power_on: Power on operation called by msm_dba_helper_power_on. Mutex
+ * protection is handled by the caller.
+ * @dev_video_on: Video on operation called by msm_dba_helper_video_on. Mutex
+ * protection is handled by the caller.
+ * @handle_interrupts: Function pointer called when an interrupt is fired. If
+ * the bridge driver uses msm_dba_helper_register_irq
+ * for handling interrupts, irq handler will call
+ * handle_interrupts to figure out the event mask.
+ * @unmask_interrupts: Function pointer called by irq handler for unmasking
+ * interrupts.
+ * @hdcp_reset: Function pointer to reset the HDCP block. This needs to be valid
+ * if HDCP monitor is used.
+ * @hdcp_retry: Function pointer to retry HDCP authentication. This needs to be
+ * valid if HDCP monitor is used.
+ * @write_reg: Function pointer to write to device specific register.
+ * @read_reg: Function pointer to read device specific register.
+ * @force_reset: Function pointer to force reset the device.
+ * @dump_debug_info: Function pointer to trigger a dump to dmesg.
+ *
+ * The device operation function pointers are used if bridge driver uses helper
+ * functions in place of some client operations. If used, the helper functions
+ * will call the device function pointers to perform device specific
+ * programming.
+ */
+struct msm_dba_device_ops {
+ int (*dev_power_on)(struct msm_dba_device_info *dev, bool on);
+ int (*dev_video_on)(struct msm_dba_device_info *dev,
+ struct msm_dba_video_cfg *cfg, bool on);
+ int (*handle_interrupts)(struct msm_dba_device_info *dev, u32 *mask);
+ int (*unmask_interrupts)(struct msm_dba_device_info *dev, u32 mask);
+ int (*hdcp_reset)(struct msm_dba_device_info *dev);
+ int (*hdcp_retry)(struct msm_dba_device_info *dev, u32 flags);
+ int (*write_reg)(struct msm_dba_device_info *dev, u32 reg, u32 val);
+ int (*read_reg)(struct msm_dba_device_info *dev, u32 reg, u32 *val);
+ int (*force_reset)(struct msm_dba_device_info *dev, u32 flags);
+ int (*dump_debug_info)(struct msm_dba_device_info *dev, u32 flags);
+};
+
+/**
+ * struct msm_dba_device_info - Device specific information
+ * @chip_name: chip name
+ * @instance_id: Instance id
+ * @caps: Capabilities of the bridge chip
+ * @dev_ops: function pointers to device specific operations
+ * @client_ops: function pointers to client operations
+ * @dev_mutex: mutex for protecting device access
+ * @hdcp_wq: HDCP workqueue for handling failures.
+ * @client_list: list head for client list
+ * @reg_fxn: Function pointer called when a client registers with dba driver
+ * @dereg_fxn: Function pointer called when a client deregisters.
+ * @power_status: current power status of device
+ * @video_status: current video status of device
+ * @audio_status: current audio status of device
+ * @hdcp_on: hdcp enable status.
+ * @enc-on: encryption enable status.
+ * @hdcp_status: hdcp link status.
+ * @hdcp_monitor_on: hdcp monitor status
+ * @register_val: debug field used to support read register.
+ *
+ * Structure containing device specific information. This structure is allocated
+ * by the bridge driver. This structure should be unique to each device.
+ *
+ */
+struct msm_dba_device_info {
+ char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN];
+ u32 instance_id;
+ struct msm_dba_capabilities caps;
+ struct msm_dba_device_ops dev_ops;
+ struct msm_dba_ops client_ops;
+ struct mutex dev_mutex;
+ struct workqueue_struct *hdcp_wq;
+ struct work_struct hdcp_work;
+ struct list_head client_list;
+ int (*reg_fxn)(struct msm_dba_client_info *client);
+ int (*dereg_fxn)(struct msm_dba_client_info *client);
+
+ bool power_status;
+ bool video_status;
+ bool audio_status;
+ bool hdcp_on;
+ bool enc_on;
+ bool hdcp_status;
+ bool hdcp_monitor_on;
+
+ /* Debug info */
+ u32 register_val;
+};
+
+/**
+ * struct msm_dba_client_info - Client specific information
+ * @dev: pointer to device information
+ * @client_name: client name
+ * @power_on: client power on status
+ * @video_on: client video on status
+ * @audio_on: client audio on status
+ * @event_mask: client event mask for callbacks.
+ * @cb: callback function for the client
+ * @cb_data: callback data pointer.
+ * @list: list pointer
+ *
+ * This structure is used to uniquely identify a client for a bridge chip. The
+ * pointer to this structure is returned as a handle from
+ * msm_dba_register_client.
+ */
+struct msm_dba_client_info {
+ struct msm_dba_device_info *dev;
+ char client_name[MSM_DBA_CLIENT_NAME_LEN];
+ bool power_on;
+ bool video_on;
+ bool audio_on;
+ u32 event_mask;
+ msm_dba_cb cb;
+ void *cb_data;
+ struct list_head list;
+};
+
+/**
+ * msm_dba_add_probed_device() - Add a new device to the probed devices list.
+ * @info: Pointer to structure containing the device information. This should be
+ * allocated by the specific bridge driver and kept until
+ * msm_dba_remove_probed_device() is called.
+ *
+ * Once a bridge chip is initialized and probed, it should add its device to the
+ * existing list of all probed display bridge chips. This list is maintained by
+ * the MSM DBA driver and is checked whenever there is a client register
+ * request.
+ */
+int msm_dba_add_probed_device(struct msm_dba_device_info *info);
+
+/**
+ * msm_dba_remove_probed_device() - Remove a device from the probed devices list
+ * @info: Pointer to structure containing the device info. This should be the
+ * same pointer used for msm_dba_add_probed_device().
+ *
+ * Bridge chip driver should call this to remove device from probed list.
+ */
+int msm_dba_remove_probed_device(struct msm_dba_device_info *info);
+
+/**
+ * msm_dba_get_probed_device() - Check if a device is present in the device list
+ * @reg: Pointer to structure containing the chip info received from the client
+ * driver
+ * @info: Pointer to the device info pointer that will be returned if the device
+ * has been found in the device list
+ *
+ * When clients of the MSM DBA driver call msm_dba_register_client(), the MSM
+ * DBA driver will use this function to check if the specific device requested
+ * by the client has been probed. If probed, function will return a pointer to
+ * the device information structure.
+ */
+int msm_dba_get_probed_device(struct msm_dba_reg_info *reg,
+ struct msm_dba_device_info **info);
+
+/**
+ * msm_dba_helper_i2c_read() - perform an i2c read transaction
+ * @client: i2c client pointer
+ * @addr: i2c slave address
+ * @reg: register where the data should be read from
+ * @buf: buffer where the read data is stored.
+ * @size: bytes to read from slave. buffer should be atleast size bytes.
+ *
+ * Helper function to perform a read from an i2c slave. Internally this calls
+ * i2c_transfer().
+ */
+int msm_dba_helper_i2c_read(struct i2c_client *client,
+ u8 addr,
+ u8 reg,
+ char *buf,
+ u32 size);
+
+/**
+ * msm_dba_helper_i2c_write_buffer() - write buffer to i2c slave.
+ * @client: i2c client pointer
+ * @addr: i2c slave address
+ * @buf: buffer where the data will be read from.
+ * @size: bytes to write.
+ *
+ * Helper function to perform a write to an i2c slave. Internally this calls
+ * i2c_transfer().
+ */
+int msm_dba_helper_i2c_write_buffer(struct i2c_client *client,
+ u8 addr,
+ u8 *buf,
+ u32 size);
+
+/**
+ * msm_dba_helper_i2c_write_byte() - write to a register on an i2c slave.
+ * @client: i2c client pointer
+ * @addr: i2c slave address
+ * @reg: slave register to write to
+ * @val: data to write.
+ *
+ * Helper function to perform a write to an i2c slave. Internally this calls
+ * i2c_transfer().
+ */
+int msm_dba_helper_i2c_write_byte(struct i2c_client *client,
+ u8 addr,
+ u8 reg,
+ u8 val);
+
+/**
+ * msm_dba_helper_power_on() - power on bridge chip
+ * @client: client handle
+ * @on: on/off
+ * @flags: flags
+ *
+ * This helper function can be used as power_on() function defined in struct
+ * msm_dba_ops. Internally, this function does some bookkeeping to figure out
+ * when to actually power on/off the device. If used, bridge driver should
+ * provide a dev_power_on to do the device specific power change.
+ */
+int msm_dba_helper_power_on(void *client, bool on, u32 flags);
+
+/**
+ * msm_dba_helper_video_on() - video on bridge chip
+ * @client: client handle
+ * @on: on/off
+ * @flags: flags
+ *
+ * This helper function can be used as video_on() function defined in struct
+ * msm_dba_ops. Internally, this function does some bookkeeping to figure out
+ * when to actually video on/off the device. If used, bridge driver should
+ * provide a dev_video_on to do the device specific video change.
+ */
+int msm_dba_helper_video_on(void *client, bool on,
+ struct msm_dba_video_cfg *cfg, u32 flags);
+
+/**
+ * msm_dba_helper_interrupts_enable() - manage interrupt callbacks
+ * @client: client handle
+ * @on: on/off
+ * @events_mask: events on which callbacks are required.
+ * @flags: flags
+ *
+ * This helper function provides the functionality needed for interrupts_enable
+ * function pointer in struct msm_dba_ops.
+ */
+int msm_dba_helper_interrupts_enable(void *client, bool on,
+ u32 events_mask, u32 flags);
+
+/**
+ * msm_dba_helper_get_caps() - return device capabilities
+ * @client: client handle
+ * @flags: flags
+ *
+ * Helper function to replace get_caps function pointer in struct msm_dba_ops
+ * structure.
+ */
+int msm_dba_helper_get_caps(void *client, struct msm_dba_capabilities *caps);
+
+/**
+ * msm_dba_helper_register_irq() - register irq and handle interrupts.
+ * @dev: pointer to device structure
+ * @irq: irq number
+ * @irq_flags: irq_flags.
+ *
+ * Helper function register an irq and handling interrupts. This will attach a
+ * threaded interrupt handler to the irq provided as input. When the irq
+ * handler is triggered, handler will call handle_interrupts in the device
+ * specific functions pointers so that bridge driver can parse the interrupt
+ * status registers and return the event mask. IRQ handler will use this event
+ * mask to provide callbacks to the clients. Once the callbacks are done,
+ * handler will call unmask_interrupts() before returning,
+ */
+int msm_dba_helper_register_irq(struct msm_dba_device_info *dev,
+ u32 irq, u32 irq_flags);
+
+/**
+ * msm_dba_register_hdcp_monitor() - kicks off monitoring for hdcp failures
+ * @dev: pointer to device structure.
+ * @enable: enable/disable
+ *
+ * Helper function to enable HDCP monitoring. This should be called only if irq
+ * is handled through msm dba helper functions.
+ */
+int msm_dba_register_hdcp_monitor(struct msm_dba_device_info *dev, bool enable);
+
+/**
+ * msm_dba_helper_sysfs_init() - create sysfs attributes for debugging
+ * @dev: pointer to struct device structure.
+ *
+ */
+int msm_dba_helper_sysfs_init(struct device *dev);
+
+/**
+ * msm_dba_helper_force_reset() - force reset bridge chip
+ * @client: client handle
+ * @flags: flags
+ *
+ * Helper function to replace force_reset function pointer in struct msm_dba_ops
+ * structure. Driver should set dev_ops.force_reset to a valid function.
+ */
+int msm_dba_helper_force_reset(void *client, u32 flags);
+#endif /* _MSM_DBA_INTERNAL_H */
diff --git a/include/video/msm_dba.h b/include/video/msm_dba.h
new file mode 100644
index 000000000000..a0a3fdfbfcf1
--- /dev/null
+++ b/include/video/msm_dba.h
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DBA_H
+#define _MSM_DBA_H
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define MSM_DBA_CHIP_NAME_MAX_LEN 20
+#define MSM_DBA_CLIENT_NAME_LEN 20
+
+#define MSM_DBA_DEFER_PROPERTY_FLAG 0x1
+#define MSM_DBA_ASYNC_FLAG 0x2
+
+/**
+ * enum msm_dba_callback_event - event types for callback notification
+ * @MSM_DBA_CB_REMOTE_INT: Event associated with remote devices on an interface
+ * that supports a bi-directional control channel.
+ * @MSM_DBA_CB_HDCP_LINK_AUTHENTICATED: Authentication session is successful.
+ * The link is authenticated and encryption
+ * can be enabled if not enabled already.
+ * @MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED: A previously authenticated link has
+ * failed. The content on the interface
+ * is no longer secure.
+ * @MSM_DBA_CB_HPD_CONNECT: Detected a cable connect event.
+ * @MSM_DBA_CB_HPD_DISCONNECT: Detected a cable disconnect event.
+ * @MSM_DBA_CB_VIDEO_FAILURE: Detected a failure with respect to video data on
+ * the interface. This is a generic failure and
+ * client should request a debug dump to debug the
+ * issue. Client can also attempt a reset to recover
+ * the device.
+ * @MSM_DBA_CB_AUDIO_FAILURE: Detected a failure with respect to audio data on
+ * the interface. This is a generic failure and
+ * client should request a debug dump. Client can
+ * also attempt a reset to recover the device.
+ * @MSM_DBA_CB_CEC_WRITE_SUCCESS: The asynchronous CEC write request is
+ * successful.
+ * @MSM_DBA_CB_CEC_WRITE_FAIL: The asynchronous CEC write request failed.
+ * @MSM_DBA_CB_CEC_READ_PENDING: There is a pending CEC read message.
+ * @MSM_DBA_CB_PRE_RESET: This callback is called just before the device is
+ * being reset.
+ * @MSM_DBA_CB_POST_RESET: This callback is called after device reset is
+ * complete and the driver has applied back all the
+ * properties.
+ *
+ * Clients for this driver can register for receiving callbacks for specific
+ * events. This enum defines the type of events supported by the driver. An
+ * event mask is typically used to denote multiple events.
+ */
+enum msm_dba_callback_event {
+ MSM_DBA_CB_REMOTE_INT = BIT(0),
+ MSM_DBA_CB_HDCP_LINK_AUTHENTICATED = BIT(1),
+ MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED = BIT(2),
+ MSM_DBA_CB_HPD_CONNECT = BIT(3),
+ MSM_DBA_CB_HPD_DISCONNECT = BIT(4),
+ MSM_DBA_CB_VIDEO_FAILURE = BIT(5),
+ MSM_DBA_CB_AUDIO_FAILURE = BIT(6),
+ MSM_DBA_CB_CEC_WRITE_SUCCESS = BIT(7),
+ MSM_DBA_CB_CEC_WRITE_FAIL = BIT(8),
+ MSM_DBA_CB_CEC_READ_PENDING = BIT(9),
+ MSM_DBA_CB_PRE_RESET = BIT(10),
+ MSM_DBA_CB_POST_RESET = BIT(11),
+};
+
+/**
+ * enum msm_dba_audio_interface_type - audio interface type
+ * @MSM_DBA_AUDIO_I2S_INTERFACE: I2S interface for audio
+ * @MSM_DBA_AUDIO_SPDIF_INTERFACE: SPDIF interface for audio
+ */
+enum msm_dba_audio_interface_type {
+ MSM_DBA_AUDIO_I2S_INTERFACE = BIT(0),
+ MSM_DBA_AUDIO_SPDIF_INTERFACE = BIT(1),
+};
+
+/**
+ * enum msm_dba_audio_format_type - audio format type
+ * @MSM_DBA_AUDIO_FMT_UNCOMPRESSED_LPCM: uncompressed format
+ * @MSM_DBA_AUDIO_FMT_COMPRESSED: compressed formats
+ */
+enum msm_dba_audio_format_type {
+ MSM_DBA_AUDIO_FMT_UNCOMPRESSED_LPCM = BIT(0),
+ MSM_DBA_AUDIO_FMT_COMPRESSED = BIT(1),
+};
+
+/**
+ * enum msm_dba_audio_sampling_rates_type - audio sampling rates
+ * @MSM_DBA_AUDIO_32KHZ: 32KHz sampling rate
+ * @MSM_DBA_AUDIO_44P1KHZ: 44.1KHz sampling rate
+ * @MSM_DBA_AUDIO_48KHZ: 48KHz sampling rate
+ * @MSM_DBA_AUDIO_96KHZ: 96KHz sampling rate
+ * @MSM_DBA_AUDIO_192KHZ: 192KHz sampling rate
+ */
+enum msm_dba_audio_sampling_rates_type {
+ MSM_DBA_AUDIO_32KHZ = BIT(0),
+ MSM_DBA_AUDIO_44P1KHZ = BIT(1),
+ MSM_DBA_AUDIO_48KHZ = BIT(2),
+ MSM_DBA_AUDIO_96KHZ = BIT(3),
+ MSM_DBA_AUDIO_192KHZ = BIT(4),
+};
+
+/**
+ * enum msm_dba_audio_word_bit_depth - audio word size
+ * @MSM_DBA_AUDIO_WORD_16BIT: 16 bits per word
+ * @MSM_DBA_AUDIO_WORD_24BIT: 24 bits per word
+ * @MSM_DBA_AUDIO_WORD_32BIT: 32 bits per word
+ */
+enum msm_dba_audio_word_bit_depth {
+ MSM_DBA_AUDIO_WORD_16BIT = BIT(0),
+ MSM_DBA_AUDIO_WORD_24BIT = BIT(1),
+ MSM_DBA_AUDIO_WORD_32BIT = BIT(2),
+};
+
+/**
+ * enum msm_dba_audio_channel_count - audio channel count
+ * @MSM_DBA_AUDIO_CHANNEL_2: 2 channel audio
+ * @MSM_DBA_AUDIO_CHANNEL_4: 4 channel audio
+ * @MSM_DBA_AUDIO_CHANNEL_8: 8 channel audio
+ */
+enum msm_dba_audio_channel_count {
+ MSM_DBA_AUDIO_CHANNEL_2 = BIT(0),
+ MSM_DBA_AUDIO_CHANNEL_4 = BIT(1),
+ MSM_DBA_AUDIO_CHANNEL_8 = BIT(2),
+};
+
+/**
+ * enum msm_dba_audio_i2s_format - i2s audio data format
+ * @MSM_DBA_AUDIO_I2S_FMT_STANDARD: Standard format
+ * @MSM_DBA_AUDIO_I2S_FMT_RIGHT_JUSTIFIED: i2s data is right justified
+ * @MSM_DBA_AUDIO_I2S_FMT_LEFT_JUSTIFIED: i2s data is left justified
+ * @MSM_DBA_AUDIO_I2S_FMT_AES3_DIRECT: AES signal format
+ */
+enum msm_dba_audio_i2s_format {
+ MSM_DBA_AUDIO_I2S_FMT_STANDARD = 0,
+ MSM_DBA_AUDIO_I2S_FMT_RIGHT_JUSTIFIED,
+ MSM_DBA_AUDIO_I2S_FMT_LEFT_JUSTIFIED,
+ MSM_DBA_AUDIO_I2S_FMT_AES3_DIRECT,
+ MSM_DBA_AUDIO_I2S_FMT_MAX,
+};
+
+enum msm_dba_video_aspect_ratio {
+ MSM_DBA_AR_UNKNOWN = 0,
+ MSM_DBA_AR_4_3,
+ MSM_DBA_AR_5_4,
+ MSM_DBA_AR_16_9,
+ MSM_DBA_AR_16_10,
+ MSM_DBA_AR_64_27,
+ MSM_DBA_AR_256_135,
+ MSM_DBA_AR_MAX
+};
+
+/**
+ * typedef *msm_dba_cb() - Prototype for callback function
+ * @data: Pointer to user data provided with register API
+ * @event: Event type associated with callback. This can be a bitmask.
+ */
+typedef void (*msm_dba_cb)(void *data, enum msm_dba_callback_event event);
+
+/**
+ * struct msm_dba_reg_info - Client information used with register API
+ * @client_name: Name of the client for debug purposes
+ * @chip_name: Bridge chip ID
+ * @instance_id: Instance ID of the bridge chip in case of multiple instances
+ * @cb: callback function called in case of events.
+ * @cb_data: pointer to a data structure that will be returned with callback
+ *
+ * msm_dba_reg_info structure will be used to provide information during
+ * registering with driver. This structure will contain the information required
+ * to identify the specific bridge chip the client wants to use.
+ *
+ * Client should also specify the callback function which needs to be called in
+ * case of events. There is an optional data field which is a pointer that will
+ * be returned as one of arguments in the callback function. This data field can
+ * be NULL if client does not wish to use it.
+ */
+struct msm_dba_reg_info {
+ char client_name[MSM_DBA_CLIENT_NAME_LEN];
+ char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN];
+ u32 instance_id;
+ msm_dba_cb cb;
+ void *cb_data;
+};
+
+/**
+ * struct msm_dba_video_caps_info - video capabilities of the bridge chip
+ * @hdcp_support: if hdcp is supported
+ * @edid_support: if reading edid from sink is supported
+ * @data_lanes_lp_support: if low power mode is supported on data lanes
+ * @clock_lanes_lp_support: If low power mode is supported on clock lanes
+ * @max_pclk_khz: maximum pixel clock supported
+ * @num_of_input_lanes: Number of input data lanes supported by the bridge chip
+ */
+struct msm_dba_video_caps_info {
+ bool hdcp_support;
+ bool edid_support;
+ bool data_lanes_lp_support;
+ bool clock_lanes_lp_support;
+ u32 max_pclk_khz;
+ u32 num_of_input_lanes;
+};
+
+/**
+ * struct msm_dba_audio_caps_info - audio capabilities of the bridge chip
+ * @audio_support: if audio is supported
+ * @audio_rates: audio sampling rates supported
+ * @audio_fmts: audio formats supported
+ */
+struct msm_dba_audio_caps_info {
+ u32 audio_support;
+ u32 audio_rates;
+ u32 audio_fmts;
+};
+
+/**
+ * struct msm_dba_capabilities - general capabilities of the bridge chip
+ * @vid_caps: video capabilities
+ * @aud_caps: audio capabilities
+ * @av_mute_support: av mute support in bridge chip
+ * @deferred_commit_support: support for deferred commit
+ */
+struct msm_dba_capabilities {
+ struct msm_dba_video_caps_info vid_caps;
+ struct msm_dba_audio_caps_info aud_caps;
+ bool av_mute_support;
+ bool deferred_commit_support;
+};
+
+/**
+ * struct msm_dba_audio_cfg - Structure for audio configuration
+ * @interface: Specifies audio interface type. Client should check the
+ * capabilities for the interfaces supported by the bridge.
+ * @format: Compressed vs Uncompressed formats.
+ * @channels: Number of channels.
+ * @i2s_fmt: I2S data packing format. This is valid only if interface is I2S.
+ */
+struct msm_dba_audio_cfg {
+ enum msm_dba_audio_interface_type interface;
+ enum msm_dba_audio_format_type format;
+ enum msm_dba_audio_channel_count channels;
+ enum msm_dba_audio_i2s_format i2s_fmt;
+};
+
+/**
+ * struct msm_dba_video_cfg - video configuration data
+ * @h_active: active width of the video signal
+ * @h_front_porch: horizontal front porch in pixels
+ * @h_pulse_width: pulse width of hsync in pixels
+ * @h_back_porch: horizontal back porch in pixels
+ * @h_polarity: polarity of hsync signal
+ * @v_active: active height of the video signal
+ * @v_front_porch: vertical front porch in lines
+ * @v_pulse_width: pulse width of vsync in lines
+ * @v_back_porch: vertical back porch in lines
+ * @v_polarity: polarity of vsync signal
+ * @pclk_khz: pixel clock in KHz
+ * @interlaced: if video is interlaced
+ * @vic: video indetification code
+ * @hdmi_mode: hdmi or dvi mode for the sink
+ * @ar: aspect ratio of the signal
+ * @num_of_input_lanes: number of input lanes in case of DSI/LVDS
+ */
+struct msm_dba_video_cfg {
+ u32 h_active;
+ u32 h_front_porch;
+ u32 h_pulse_width;
+ u32 h_back_porch;
+ bool h_polarity;
+ u32 v_active;
+ u32 v_front_porch;
+ u32 v_pulse_width;
+ u32 v_back_porch;
+ bool v_polarity;
+ u32 pclk_khz;
+ bool interlaced;
+ u32 vic;
+ bool hdmi_mode;
+ enum msm_dba_video_aspect_ratio ar;
+ u32 num_of_input_lanes;
+};
+
+/**
+ * struct msm_dba_ops- operation supported by bridge chip
+ * @get_caps: returns the bridge chip capabilities
+ * DEFER and ASYNC flags are not supported.
+ * @power_on: powers on/off the bridge chip. This usually involves turning on
+ * the power regulators and bringing the chip out of reset. Chip
+ * should be capable of raising interrupts at this point.
+ * DEFER and ASYNC flags are supported.
+ * @video_on: turn on/off video stream. This function also requires the video
+ * timing information that might be needed for programming the bridge
+ * chip.
+ * DEFER flag is supported.
+ * ASYNC flag is not supported.
+ * @audio_on: turn on/off audio stream.
+ * DEFER flag is supported.
+ * ASYNC flag is not supported.
+ * @configure_audio: setup audio configuration
+ * DEFER flag is supported.
+ * ASYNC flag is not supported.
+ * @av_mute: controls av mute functionalities if supported. AV mute is different
+ * from audio_on and video_on where in even though the actual data is
+ * sent, mute is specified through control packets.
+ * DEFER flag is supported.
+ * ASYNC flag is not supported.
+ * @interupts_enable: enables interrupts to get event callbacks. Clients need
+ * to specify an event mask of the events they are
+ * interested in. If a client provides an event as part of
+ * the mask, it will receive the interrupt regardless of the
+ * client modifying the property.
+ * DEFER flag is supported.
+ * ASYNC flag is not supported.
+ * @hdcp_enable: enable/disable hdcp. If HDCP is enabled, this function will
+ * start a new authentication session. There is a separate
+ * argument for enabling encryption. Encryption can be enabled any
+ * time after HDCP has been fully authenticated. This function
+ * will support an asynchronous mode where calling this function
+ * will kick off HDCP and return to the caller. Caller has to wait
+ * for MSM_DBA_CB_HDCP_SUCCESS callback to ensure link is
+ * authenticated.
+ * DEFER flag is not supported.
+ * ASYNC flag is supported.
+ * @hdcp_get_ksv_list_size: returns the KSV list size. In case of a simple sink
+ * the size will be 1. In case of a repeater, this can
+ * be more than one.
+ * DEFER and ASYNC flags are not supported.
+ * @hdcp_get_ksv_list: return the KSV list. Client can query the KSV information
+ * from the bridge. Client should call
+ * hdcp_get_ksv_list_size first and then allocate 40*size
+ * bytes to hold all the KSVs.
+ * DEFER and ASYNC flags are not supported.
+ * @hdmi_cec_write: perform a CEC write. For bridges with HDMI as output
+ * interface, this function allows clients to send a CEC
+ * message. Client should pack the data according to the CEC
+ * specification and provide the final buffer. Since CEC writes
+ * can take longer time to ascertaining if they are successful,
+ * this function supports the ASYNC flag. Driver will return
+ * either MSM_DBA_CB_CEC_WRITE_SUCCESS or
+ * MSM_DBA_CB_CEC_WRITE_FAIL callbacks.
+ * DEFER is not supported.
+ * ASYNC flag is supported.
+ * @hdmi_cec_read: get a pending CEC read message. In case of an incoming CEC
+ * message, driver will return MSM_DBA_CB_CEC_READ_PENDING
+ * callback. On getting this event callback, client should call
+ * hdmi_cec_read to get the message. The buffer should at least
+ * be 15 bytes or more. Client should read the CEC message from
+ * a thread different from the callback.
+ * DEFER and ASYNC flags are not supported.
+ * @get_edid_size: returns size of the edid.
+ * DEFER and ASYNC flags are not supported.
+ * @get_raw_edid: returns raw edid data.
+ * DEFER and ASYNC flags are not supported.
+ * @enable_remote_comm: enable/disable remote communication. Some interfaces
+ * like FPDLINK III support a bi-directional control
+ * channel that could be used to send control data using an
+ * I2C or SPI protocol. This Function will enable this
+ * control channel if supported.
+ * DEFER and ASYNC flags are not supported.
+ * @add_remote_device: add slaves on remote side for enabling communication. For
+ * interfaces that support bi directional control channel,
+ * this function allows clients to specify slave IDs of
+ * devices on remote bus. Messages addressed to these IDs
+ * will be trapped by the bridge chip and put on the remote
+ * bus.
+ * DEFER and ASYNC flags are not supported.
+ * @commit_deferred_props: commits deferred properties
+ * DEFER and ASYNC flags are not supported.
+ * @force_reset: reset the device forcefully. In case the device goes into a bad
+ * state, a client can force reset to try and recover the device.
+ * The reset will be applied in spite of different configurations
+ * from other clients. Driver will apply all the properties that
+ * have been applied so far after the reset is complete. In case
+ * of multiple clients, driver will issue a reset callback.
+ * @dump_debug_info: dumps debug information to dmesg.
+ *
+ * The msm_dba_ops structure represents a set of operations that can be
+ * supported by each bridge chip. Depending on the functionality supported by a
+ * specific bridge chip, some of the operations need not be supported. For
+ * example if a bridge chip does not support reading EDID from a sink device,
+ * get_edid_size and get_raw_edid can be NULL.
+ *
+ * Deferring properties: The deferred flag allows us to address any quirks with
+ * respect to specific bridge chips. If there is a need for some properties to
+ * be committed together, turning on video and audio at the same time, the
+ * deferred flag can be used. Properties that are set using a DEFER flag will
+ * not be committed to hardware until commit_deferred_props() function is
+ * called.
+ *
+ */
+struct msm_dba_ops {
+ int (*get_caps)(void *client,
+ struct msm_dba_capabilities *caps);
+
+ int (*power_on)(void *client,
+ bool on,
+ u32 flags);
+
+ int (*video_on)(void *client,
+ bool on,
+ struct msm_dba_video_cfg *cfg,
+ u32 flags);
+
+ int (*audio_on)(void *client,
+ bool on,
+ u32 flags);
+
+ int (*configure_audio)(void *client,
+ struct msm_dba_audio_cfg *cfg,
+ u32 flags);
+
+ int (*av_mute)(void *client,
+ bool video_mute,
+ bool audio_mute,
+ u32 flags);
+
+ int (*interrupts_enable)(void *client,
+ bool on,
+ u32 event_mask,
+ u32 flags);
+
+ int (*hdcp_enable)(void *client,
+ bool hdcp_on,
+ bool enc_on,
+ u32 flags);
+
+ int (*hdcp_get_ksv_list_size)(void *client,
+ u32 *count,
+ u32 flags);
+
+ int (*hdcp_get_ksv_list)(void *client,
+ u32 count,
+ char *buf,
+ u32 flags);
+
+ int (*hdmi_cec_write)(void *client,
+ u32 size,
+ char *buf,
+ u32 flags);
+
+ int (*hdmi_cec_read)(void *client,
+ u32 *size,
+ char *buf,
+ u32 flags);
+
+ int (*get_edid_size)(void *client,
+ u32 *size,
+ u32 flags);
+
+ int (*get_raw_edid)(void *client,
+ u32 size,
+ char *buf,
+ u32 flags);
+
+ int (*enable_remote_comm)(void *client,
+ bool on,
+ u32 flags);
+
+ int (*add_remote_device)(void *client,
+ u32 *slave_ids,
+ u32 count,
+ u32 flags);
+
+ int (*commit_deferred_props)(void *client,
+ u32 flags);
+
+ int (*force_reset)(void *client, u32 flags);
+ int (*dump_debug_info)(void *client, u32 flags);
+};
+
+/**
+ * msm_dba_register_client() - Allows a client to register with the driver.
+ * @info: Client information along with the bridge chip id the client wishes to
+ * program.
+ * @ops: Function pointers to bridge chip operations. Some function pointers can
+ * be NULL depending on the functionalities supported by bridge chip.
+ *
+ * The register API supports multiple clients to register for the same bridge
+ * chip. If Successful, this will return a pointer that should be used as a
+ * handle for all subsequent function calls.
+ */
+void *msm_dba_register_client(struct msm_dba_reg_info *info,
+ struct msm_dba_ops *ops);
+
+/**
+ * msm_dba_deregister_client() - Allows client to de-register with the driver.
+ * @client: client handle returned by register API.
+ *
+ * This function will release all the resources used by a particular client. If
+ * it is the only client using the bridge chip, the bridge chip will be powered
+ * down and put into reset.
+ */
+int msm_dba_deregister_client(void *client);
+
+#endif /* _MSM_DBA_H */