From bed7a261e15411fc98f8e49f4d10d3aa72f8b4f7 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 7 Jan 2020 15:20:14 -0800 Subject: [PATCH] platform: msm: add support for external display module Add support for the external display module, which is a utility to manage display interface and audio codec interactions associated with the cable connect/disconnect events of the display interfaces. The helper class is responsible for routing operations called by the audio codec to a particular display, and for creating and updating the audio switch nodes. This implementation is based on a snapshot of the external display module as of this commit 104eef6e1fe8d4d2 ("platform: msm: Remove unused local count variable") on kernel-4.19. Change-Id: I919adf3fda110ecba8d7054a1385e2cbdaca2467 Signed-off-by: Tatenda Chipeperekwa --- drivers/platform/msm/Kconfig | 10 + drivers/platform/msm/Makefile | 2 + drivers/platform/msm/msm_ext_display.c | 689 +++++++++++++++++++++++++ include/linux/msm_ext_display.h | 205 ++++++++ 4 files changed, 906 insertions(+) create mode 100644 drivers/platform/msm/msm_ext_display.c create mode 100644 include/linux/msm_ext_display.h diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 296d89396801..6319c990b6a3 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -3,6 +3,16 @@ menu "Qualcomm technologies inc. MSM specific device drivers" depends on ARCH_QCOM +config MSM_EXT_DISPLAY + bool "MSM External Display Driver" + depends on DRM && QGKI + help + Enabling this option adds MSM External Display Driver. + External Display driver was added to support the communication + between external display driver and its counterparts. + This should be enabled to support audio & video over HDMI or + DP for hot pluggable sink devices. + config SPS tristate "SPS support" select GENERIC_ALLOCATOR diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 86bb615e199a..c76589e5e970 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -3,6 +3,8 @@ # # Makefile for the MSM specific device drivers. # + +obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o obj-$(CONFIG_GSI) += gsi/ obj-$(CONFIG_IPA3) += ipa/ obj-$(CONFIG_MSM_GENI_SE) += msm-geni-se.o diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c new file mode 100644 index 000000000000..45284c12296f --- /dev/null +++ b/drivers/platform/msm/msm_ext_display.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct msm_ext_disp_list { + struct msm_ext_disp_init_data *data; + struct list_head list; +}; + +struct msm_ext_disp { + struct msm_ext_disp_data ext_disp_data; + struct platform_device *pdev; + struct msm_ext_disp_codec_id current_codec; + struct msm_ext_disp_audio_codec_ops *ops; + struct extcon_dev *audio_sdev[MSM_EXT_DISP_MAX_CODECS]; + bool audio_session_on; + struct list_head display_list; + struct mutex lock; +}; + +static const unsigned int msm_ext_disp_supported_cable[] = { + EXTCON_DISP_DP, + EXTCON_DISP_HDMI, + EXTCON_NONE, +}; + +static int msm_ext_disp_extcon_register(struct msm_ext_disp *ext_disp, int id) +{ + int ret = 0; + + if (!ext_disp || !ext_disp->pdev || id >= MSM_EXT_DISP_MAX_CODECS) { + pr_err("invalid params\n"); + return -EINVAL; + } + + ext_disp->audio_sdev[id] = devm_extcon_dev_allocate( + &ext_disp->pdev->dev, + msm_ext_disp_supported_cable); + if (IS_ERR(ext_disp->audio_sdev[id])) + return PTR_ERR(ext_disp->audio_sdev[id]); + + ret = devm_extcon_dev_register(&ext_disp->pdev->dev, + ext_disp->audio_sdev[id]); + if (ret) { + pr_err("audio registration failed\n"); + return ret; + } + + pr_debug("extcon registration done\n"); + + return ret; +} + +static void msm_ext_disp_extcon_unregister(struct msm_ext_disp *ext_disp, + int id) +{ + if (!ext_disp || !ext_disp->pdev || id >= MSM_EXT_DISP_MAX_CODECS) { + pr_err("Invalid params\n"); + return; + } + + devm_extcon_dev_unregister(&ext_disp->pdev->dev, + ext_disp->audio_sdev[id]); +} + +static const char *msm_ext_disp_name(enum msm_ext_disp_type type) +{ + switch (type) { + case EXT_DISPLAY_TYPE_HDMI: + return "EXT_DISPLAY_TYPE_HDMI"; + case EXT_DISPLAY_TYPE_DP: + return "EXT_DISPLAY_TYPE_DP"; + default: return "???"; + } +} + +static int msm_ext_disp_add_intf_data(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_init_data *data) +{ + struct msm_ext_disp_list *node; + + if (!ext_disp || !data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->data = data; + + list_add(&node->list, &ext_disp->display_list); + + pr_debug("Added new display (%s) ctld (%d) stream (%d)\n", + msm_ext_disp_name(data->codec.type), + data->codec.ctrl_id, data->codec.stream_id); + + return 0; +} + +static int msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_init_data *data) +{ + struct msm_ext_disp_list *node; + struct list_head *pos = NULL; + + if (!ext_disp || !data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + list_for_each(pos, &ext_disp->display_list) { + node = list_entry(pos, struct msm_ext_disp_list, list); + if (node->data == data) { + list_del(pos); + pr_debug("Deleted the intf data\n"); + return 0; + } + } + + pr_debug("Intf data not present for delete op\n"); + + return 0; +} + +static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_codec_id *codec, + struct msm_ext_disp_init_data **data) +{ + int ret = 0; + struct msm_ext_disp_list *node; + struct list_head *position = NULL; + + if (!ext_disp || !data || !codec) { + pr_err("Invalid params\n"); + ret = -EINVAL; + goto end; + } + + *data = NULL; + list_for_each(position, &ext_disp->display_list) { + node = list_entry(position, struct msm_ext_disp_list, list); + if (node->data->codec.type == codec->type && + node->data->codec.stream_id == codec->stream_id && + node->data->codec.ctrl_id == codec->ctrl_id) { + *data = node->data; + break; + } + } + + if (!*data) + ret = -ENODEV; +end: + return ret; +} + +static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state new_state) +{ + int ret = 0; + int state; + struct extcon_dev *audio_sdev; + + if (!ext_disp->ops) { + pr_err("codec not registered, skip notification\n"); + ret = -EPERM; + goto end; + } + + audio_sdev = ext_disp->audio_sdev[codec->stream_id]; + + state = extcon_get_state(audio_sdev, codec->type); + if (state == !!new_state) { + ret = -EEXIST; + pr_debug("same state\n"); + goto end; + } + + ret = extcon_set_state_sync(audio_sdev, + codec->type, !!new_state); + if (ret) + pr_err("Failed to set state. Error = %d\n", ret); + else + pr_debug("state changed to %d\n", new_state); + +end: + return ret; +} + +static struct msm_ext_disp *msm_ext_disp_validate_and_get( + struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state) +{ + struct msm_ext_disp_data *ext_disp_data; + struct msm_ext_disp *ext_disp; + + if (!pdev) { + pr_err("invalid platform device\n"); + goto err; + } + + if (!codec || + codec->type >= EXT_DISPLAY_TYPE_MAX || + codec->ctrl_id != 0 || + codec->stream_id >= MSM_EXT_DISP_MAX_CODECS) { + pr_err("invalid display codec id\n"); + goto err; + } + + if (state < EXT_DISPLAY_CABLE_DISCONNECT || + state >= EXT_DISPLAY_CABLE_STATE_MAX) { + pr_err("invalid HPD state (%d)\n", state); + goto err; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("invalid drvdata\n"); + goto err; + } + + ext_disp = container_of(ext_disp_data, + struct msm_ext_disp, ext_disp_data); + + return ext_disp; +err: + return ERR_PTR(-EINVAL); +} + +static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_codec_id *codec) +{ + int ret = 0; + struct msm_ext_disp_init_data *data = NULL; + + ret = msm_ext_disp_get_intf_data(ext_disp, codec, &data); + if (ret || !data) { + pr_err("Display not found (%s) ctld (%d) stream (%d)\n", + msm_ext_disp_name(codec->type), + codec->ctrl_id, codec->stream_id); + goto end; + } + + if (ext_disp->ops) { + *ext_disp->ops = data->codec_ops; + ext_disp->current_codec = *codec; + + /* update pdev for interface to use */ + ext_disp->ext_disp_data.intf_pdev = data->pdev; + ext_disp->ext_disp_data.intf_data = data->intf_data; + } + +end: + return ret; +} + +static int msm_ext_disp_audio_config(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state) +{ + int ret = 0; + struct msm_ext_disp *ext_disp; + + ext_disp = msm_ext_disp_validate_and_get(pdev, codec, state); + if (IS_ERR(ext_disp)) { + ret = PTR_ERR(ext_disp); + goto end; + } + + if (state == EXT_DISPLAY_CABLE_CONNECT) { + ret = msm_ext_disp_select_audio_codec(pdev, codec); + if (ret) + pr_err("error setting audio codec\n"); + } else { + mutex_lock(&ext_disp->lock); + if (ext_disp->ops) + memset(ext_disp->ops, 0, sizeof(*ext_disp->ops)); + + pr_debug("codec ops cleared for %s\n", + msm_ext_disp_name(ext_disp->current_codec.type)); + + ext_disp->current_codec.type = EXT_DISPLAY_TYPE_MAX; + mutex_unlock(&ext_disp->lock); + } +end: + return ret; +} + +static int msm_ext_disp_audio_notify(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state) +{ + int ret = 0; + struct msm_ext_disp *ext_disp; + + ext_disp = msm_ext_disp_validate_and_get(pdev, codec, state); + if (IS_ERR(ext_disp)) { + ret = PTR_ERR(ext_disp); + goto end; + } + + mutex_lock(&ext_disp->lock); + ret = msm_ext_disp_process_audio(ext_disp, codec, state); + mutex_unlock(&ext_disp->lock); +end: + return ret; +} + +static void msm_ext_disp_ready_for_display(struct msm_ext_disp *ext_disp) +{ + int ret; + struct msm_ext_disp_init_data *data = NULL; + + if (!ext_disp) { + pr_err("invalid input\n"); + return; + } + + ret = msm_ext_disp_get_intf_data(ext_disp, + &ext_disp->current_codec, &data); + if (ret) { + pr_err("%s not found\n", + msm_ext_disp_name(ext_disp->current_codec.type)); + return; + } + + *ext_disp->ops = data->codec_ops; + data->codec_ops.ready(ext_disp->pdev); +} + +int msm_hdmi_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops) +{ + return msm_ext_disp_register_audio_codec(pdev, ops); +} + +/** + * Register audio codec ops to display driver + * for HDMI/Display Port usecase support. + * + * @return 0 on success, negative value on error + * + */ +int msm_ext_disp_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_data *ext_disp_data = NULL; + + if (!pdev || !ops) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + ext_disp = container_of(ext_disp_data, struct msm_ext_disp, + ext_disp_data); + + mutex_lock(&ext_disp->lock); + + if (ext_disp->ops) { + pr_err("Codec already registered\n"); + ret = -EINVAL; + goto end; + } + + ext_disp->ops = ops; + + pr_debug("audio codec registered\n"); + +end: + mutex_unlock(&ext_disp->lock); + if (ext_disp->current_codec.type != EXT_DISPLAY_TYPE_MAX) + msm_ext_disp_ready_for_display(ext_disp); + + return ret; +} +EXPORT_SYMBOL(msm_ext_disp_register_audio_codec); + +int msm_ext_disp_select_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_data *ext_disp_data = NULL; + + if (!pdev || !codec) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + ext_disp = container_of(ext_disp_data, struct msm_ext_disp, + ext_disp_data); + + mutex_lock(&ext_disp->lock); + + if (!ext_disp->ops) { + pr_err("Codec is not registered\n"); + ret = -EINVAL; + goto end; + } + + ret = msm_ext_disp_update_audio_ops(ext_disp, codec); + +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} +EXPORT_SYMBOL(msm_ext_disp_select_audio_codec); + +static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data) +{ + struct msm_ext_disp_audio_codec_ops *ops; + + if (!init_data) { + pr_err("Invalid init_data\n"); + return -EINVAL; + } + + if (!init_data->pdev) { + pr_err("Invalid display intf pdev\n"); + return -EINVAL; + } + + if (init_data->codec.type >= EXT_DISPLAY_TYPE_MAX || + init_data->codec.ctrl_id != 0 || + init_data->codec.stream_id >= MSM_EXT_DISP_MAX_CODECS) { + pr_err("Invalid codec info type(%d), ctrl(%d) stream(%d)\n", + init_data->codec.type, + init_data->codec.ctrl_id, + init_data->codec.stream_id); + return -EINVAL; + } + + ops = &init_data->codec_ops; + + if (!ops->audio_info_setup || !ops->get_audio_edid_blk || + !ops->cable_status || !ops->get_intf_id || + !ops->teardown_done || !ops->acknowledge || + !ops->ready) { + pr_err("Invalid codec operation pointers\n"); + return -EINVAL; + } + + return 0; +} + +int msm_ext_disp_register_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data) +{ + int ret = 0; + struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_data *ext_disp_data = NULL; + + if (!pdev || !init_data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + ext_disp = container_of(ext_disp_data, struct msm_ext_disp, + ext_disp_data); + + mutex_lock(&ext_disp->lock); + + ret = msm_ext_disp_validate_intf(init_data); + if (ret) + goto end; + + ret = msm_ext_disp_get_intf_data(ext_disp, &init_data->codec, &data); + if (!ret) { + pr_err("%s already registered. ctrl(%d) stream(%d)\n", + msm_ext_disp_name(init_data->codec.type), + init_data->codec.ctrl_id, + init_data->codec.stream_id); + goto end; + } + + ret = msm_ext_disp_add_intf_data(ext_disp, init_data); + if (ret) + goto end; + + init_data->intf_ops.audio_config = msm_ext_disp_audio_config; + init_data->intf_ops.audio_notify = msm_ext_disp_audio_notify; + + pr_debug("%s registered. ctrl(%d) stream(%d)\n", + msm_ext_disp_name(init_data->codec.type), + init_data->codec.ctrl_id, + init_data->codec.stream_id); +end: + mutex_unlock(&ext_disp->lock); + return ret; +} + +int msm_ext_disp_deregister_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_data *ext_disp_data = NULL; + + if (!pdev || !init_data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + ext_disp = container_of(ext_disp_data, struct msm_ext_disp, + ext_disp_data); + + mutex_lock(&ext_disp->lock); + + ret = msm_ext_disp_remove_intf_data(ext_disp, init_data); + if (ret) + goto end; + + init_data->intf_ops.audio_config = NULL; + init_data->intf_ops.audio_notify = NULL; + + pr_debug("%s deregistered\n", + msm_ext_disp_name(init_data->codec.type)); +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +static int msm_ext_disp_probe(struct platform_device *pdev) +{ + int ret = 0, id; + struct device_node *of_node = NULL; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("No platform device found\n"); + ret = -ENODEV; + goto end; + } + + of_node = pdev->dev.of_node; + if (!of_node) { + pr_err("No device node found\n"); + ret = -ENODEV; + goto end; + } + + ext_disp = devm_kzalloc(&pdev->dev, sizeof(*ext_disp), GFP_KERNEL); + if (!ext_disp) { + ret = -ENOMEM; + goto end; + } + + platform_set_drvdata(pdev, &ext_disp->ext_disp_data); + ext_disp->pdev = pdev; + + for (id = 0; id < MSM_EXT_DISP_MAX_CODECS; id++) { + ret = msm_ext_disp_extcon_register(ext_disp, id); + if (ret) + goto child_node_failure; + } + + ret = of_platform_populate(of_node, NULL, NULL, &pdev->dev); + if (ret) { + pr_err("Failed to add child devices. Error = %d\n", ret); + goto child_node_failure; + } else { + pr_debug("%s: Added child devices.\n", __func__); + } + + mutex_init(&ext_disp->lock); + + INIT_LIST_HEAD(&ext_disp->display_list); + ext_disp->current_codec.type = EXT_DISPLAY_TYPE_MAX; + + return ret; + +child_node_failure: + for (id = 0; id < MSM_EXT_DISP_MAX_CODECS; id++) + msm_ext_disp_extcon_unregister(ext_disp, id); + + devm_kfree(&ext_disp->pdev->dev, ext_disp); +end: + return ret; +} + +static int msm_ext_disp_remove(struct platform_device *pdev) +{ + int ret = 0, id; + struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_data *ext_disp_data = NULL; + + if (!pdev) { + pr_err("No platform device\n"); + ret = -ENODEV; + goto end; + } + + ext_disp_data = platform_get_drvdata(pdev); + if (!ext_disp_data) { + pr_err("No drvdata found\n"); + ret = -ENODEV; + goto end; + } + + ext_disp = container_of(ext_disp_data, struct msm_ext_disp, + ext_disp_data); + + for (id = 0; id < MSM_EXT_DISP_MAX_CODECS; id++) + msm_ext_disp_extcon_unregister(ext_disp, id); + + mutex_destroy(&ext_disp->lock); + devm_kfree(&ext_disp->pdev->dev, ext_disp); + +end: + return ret; +} + +static const struct of_device_id msm_ext_dt_match[] = { + {.compatible = "qcom,msm-ext-disp",}, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, msm_ext_dt_match); + +static struct platform_driver this_driver = { + .probe = msm_ext_disp_probe, + .remove = msm_ext_disp_remove, + .driver = { + .name = "msm-ext-disp", + .of_match_table = msm_ext_dt_match, + }, +}; + +static int __init msm_ext_disp_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&this_driver); + if (ret) + pr_err("failed, ret = %d\n", ret); + + return ret; +} + +subsys_initcall(msm_ext_disp_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM External Display"); diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h new file mode 100644 index 000000000000..ff949d9fda10 --- /dev/null +++ b/include/linux/msm_ext_display.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _MSM_EXT_DISPLAY_H_ +#define _MSM_EXT_DISPLAY_H_ + +#include +#include +#include + +#define AUDIO_ACK_SET_ENABLE BIT(5) +#define AUDIO_ACK_ENABLE BIT(4) +#define AUDIO_ACK_CONNECT BIT(0) + +#define MSM_EXT_DISP_MAX_CODECS 2 + +/* + * Flags to be used with the HPD operation of the external display + * interface: + * MSM_EXT_DISP_HPD_AUDIO: audio will be routed to external display + * MSM_EXT_DISP_HPD_VIDEO: video will be routed to external display + */ +#define MSM_EXT_DISP_HPD_AUDIO BIT(0) +#define MSM_EXT_DISP_HPD_VIDEO BIT(1) + +/** + * struct ext_disp_cable_notify - cable notify handler structure + * @link: a link for the linked list + * @status: current status of HDMI/DP cable connection + * @hpd_notify: callback function to provide cable status + */ +struct ext_disp_cable_notify { + struct list_head link; + int status; + void (*hpd_notify)(struct ext_disp_cable_notify *h); +}; + +struct msm_ext_disp_audio_edid_blk { + u8 *audio_data_blk; + unsigned int audio_data_blk_size; /* in bytes */ + u8 *spk_alloc_data_blk; + unsigned int spk_alloc_data_blk_size; /* in bytes */ +}; + +struct msm_ext_disp_audio_setup_params { + u32 sample_rate_hz; + u32 num_of_channels; + u32 channel_allocation; + u32 level_shift; + bool down_mix; + u32 sample_present; +}; + +/* + * External Display identifier for use to determine which interface + * the audio driver is interacting with. + */ +enum msm_ext_disp_type { + EXT_DISPLAY_TYPE_HDMI = EXTCON_DISP_HDMI, + EXT_DISPLAY_TYPE_DP = EXTCON_DISP_DP, + EXT_DISPLAY_TYPE_MAX = 0xFFFFFFFF +}; + +/* + * External Display cable state used by display interface to indicate + * connect/disconnect of interface. + */ +enum msm_ext_disp_cable_state { + EXT_DISPLAY_CABLE_DISCONNECT, + EXT_DISPLAY_CABLE_CONNECT, + EXT_DISPLAY_CABLE_STATE_MAX +}; + +/** + * External Display power state used by display interface to indicate + * power on/off of the interface. + */ +enum msm_ext_disp_power_state { + EXT_DISPLAY_POWER_OFF, + EXT_DISPLAY_POWER_ON, + EXT_DISPLAY_POWER_MAX +}; + +/** + * struct msm_ext_disp_codec_id - codec information + * @type: external display type + * @ctrl_id: controller id + * @stream_id: stream_id + */ +struct msm_ext_disp_codec_id { + enum msm_ext_disp_type type; + int ctrl_id; + int stream_id; +}; + +/** + * struct msm_ext_disp_intf_ops - operations exposed to display interface + * @audio_config: configures the audio operations exposed to codec driver + * @audio_notify: notifies the audio connection state to user modules. + * @video_notify: notifies the video connection state to user modules. + */ +struct msm_ext_disp_intf_ops { + int (*audio_config)(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state); + + int (*audio_notify)(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state); + + int (*video_notify)(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec, + enum msm_ext_disp_cable_state state); +}; + +/** + * struct msm_ext_disp_audio_codec_ops - operations exposed to audio codec + * @audio_info_setup: configure audio on interface + * @get_audio_edid_blk: retrieve audio edid block + * @cable_status: cable connected/disconnected + * @get_intf_id: id of connected interface + * @teardown_done: audio session teardown done by qdsp + * @acknowledge: acknowledge audio status received by user modules + * @ready: notify audio when codec driver is ready. + */ +struct msm_ext_disp_audio_codec_ops { + int (*audio_info_setup)(struct platform_device *pdev, + struct msm_ext_disp_audio_setup_params *params); + int (*get_audio_edid_blk)(struct platform_device *pdev, + struct msm_ext_disp_audio_edid_blk *blk); + int (*cable_status)(struct platform_device *pdev, u32 vote); + int (*get_intf_id)(struct platform_device *pdev); + void (*teardown_done)(struct platform_device *pdev); + int (*acknowledge)(struct platform_device *pdev, u32 ack); + int (*ready)(struct platform_device *pdev); +}; + +/** + * struct msm_ext_disp_init_data - data needed to register a display interface + * @type: external display type + * @intf_ops: external display interface operations + * @codec_ops: audio codec operations + * @pdev: platform device instance of the interface driver + * @intf_data: interface specific data + */ +struct msm_ext_disp_init_data { + struct msm_ext_disp_codec_id codec; + struct msm_ext_disp_intf_ops intf_ops; + struct msm_ext_disp_audio_codec_ops codec_ops; + struct platform_device *pdev; + void *intf_data; +}; + +/** + * struct msm_ext_disp_data - data needed by interface modules + * @intf_pdev: platform device instance of the interface + * @intf_data: data related to interface module + */ +struct msm_ext_disp_data { + struct platform_device *intf_pdev; + void *intf_data; +}; + +/** + * msm_ext_disp_register_audio_codec() - audio codec registration + * @pdev: platform device pointer + * @codec_ops: audio codec operations + */ +int msm_ext_disp_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops); + +/** + * msm_ext_disp_select_audio_codec() - select audio codec + * @pdev: platform device pointer + * @codec: codec id information + */ +int msm_ext_disp_select_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_codec_id *codec); + +/** + * msm_hdmi_register_audio_codec() - wrapper for hdmi audio codec + * registration + * @pdev: platform device pointer + * @codec_ops: audio codec operations + */ +int msm_hdmi_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops); + +/** + * msm_ext_disp_register_intf() - display interface registration + * @init_data: data needed to register the display interface + */ +int msm_ext_disp_register_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data); + +/** + * msm_ext_disp_deregister_intf() - display interface deregistration + * @init_data: data needed to deregister the display interface + */ +int msm_ext_disp_deregister_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data); + +#endif /*_MSM_EXT_DISPLAY_H_*/