summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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
9 files changed, 1358 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 */