diff options
| -rw-r--r-- | drivers/video/fbdev/msm/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/Makefile | 3 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/msm_dba.c | 146 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/msm_dba_debug.c | 299 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c | 445 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/msm_dba_init.c | 131 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_dba/msm_dba_internal.h | 317 | ||||
| -rw-r--r-- | include/video/msm_dba.h | 503 |
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(®ister_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(®ister_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(®ister_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(®ister_mutex); + return ERR_PTR(rc); + } + } + + mutex_unlock(®ister_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(®ister_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(®ister_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, ®); + 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, ®); + 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 = ® + + 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 */ |
