Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e935c2a authored by Sandeep Panda's avatar Sandeep Panda Committed by Gerrit - the friendly Code Review server
Browse files

msm: mdss: separate out DBA and CEC functionality from DSI



Currently, DBA (Display Bridge Abstract) related functionality
is being used in DSI files. Carve out DBA related functionality
into a new file which serves as a utility module and can be used
by any MDSS driver.

Define CEC on/enable functions in DBA (Display Bridge Abstract) so that
clients can enable disable CEC based on other dependent CEC modules.
Separate out CEC abstract data with CEC driver data and initialize and
release corresponding modules properly.

Change-Id: I84f53d99547dcd4ce0b8275401b03ed8e96e14d5
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
Signed-off-by: default avatarSandeep Panda <spanda@codeaurora.org>
parent 9c186765
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@ Optional properties:
- adi,irq-gpio:				Main IRQ gpio mapping
- adi,hpd-irq-gpio:			HPD IRQ gpio mapping
- adi,switch-gpio:			DSI switch gpio mapping
- qcom,supply-names:			Regulator names that supply 5v to bridge chip
- qcom,min-voltage-level		Minimum voltage level to be supplied to bridge chip
- qcom,max-voltage-level		Maximum voltage level to be supplied to bridge chip
- qcom,enable-load			Load current to bridge chip when enabled
- qcom,disable-load			Load current to bridge chip when disabled

Example:
&soc {
@@ -35,6 +40,12 @@ Example:
			adi,irq-gpio = <&msm_gpio 31 0x2002>;
			adi,hpd-irq-gpio = <&msm_gpio 20 0x2003>;
			adi,switch-gpio = <&msm_gpio 32 0x0>;
			hpd-5v-en-supply = <&adv_vreg>;
			qcom,supply-names = "hpd-5v-en";
			qcom,min-voltage-level = <0>;
			qcom,max-voltage-level = <0>;
			qcom,enable-load = <0>;
			qcom,disable-load = <0>;
		};
	};
};
+3 −0
Original line number Diff line number Diff line
@@ -446,6 +446,8 @@ Optional properites:
- qcom,mdss-dsc-block-prediction-enable: A boolean value to enable/disable the block prediction at decoder.
- qcom,mdss-dsc-config-by-manufacture-cmd: A boolean to indicates panel use manufacture command to setup pps
					instead of standard dcs type 0x0A.
- qcom,dba-panel:	Indicates whether the current panel is used as a display bridge
					to a non-DSI interface.
- qcom,adjust-timer-wakeup-ms:		An integer value to indicate the timer delay(in ms) to accommodate
					s/w delay while configuring the event timer wakeup logic.

@@ -661,6 +663,7 @@ Example:
		};

		qcom,config-select = <&dsi_sim_vid_config0>;
		qcom,dba-panel;

		dsi_sim_vid_config0: config0 {
			qcom,lm-split = <360 360>;
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_cec_core.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o
obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o
obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o

+768 −0
Original line number Diff line number Diff line
/* 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.
 *
 */

#define pr_fmt(fmt)	"%s: " fmt, __func__

#include <video/msm_dba.h>
#include <linux/switch.h>

#include "mdss_dba_utils.h"
#include "mdss_hdmi_edid.h"
#include "mdss_cec_core.h"
#include "mdss_fb.h"

/* standard cec buf size + 1 byte specific to driver */
#define CEC_BUF_SIZE    (MAX_CEC_FRAME_SIZE + 1)
#define MAX_SWITCH_NAME_SIZE        5
#define MSM_DBA_MAX_PCLK 148500

struct mdss_dba_utils_data {
	struct msm_dba_ops ops;
	bool hpd_state;
	bool audio_switch_registered;
	bool display_switch_registered;
	struct switch_dev sdev_display;
	struct switch_dev sdev_audio;
	struct kobject *kobj;
	struct mdss_panel_info *pinfo;
	void *dba_data;
	void *edid_data;
	void *cec_abst_data;
	u8 *edid_buf;
	u32 edid_buf_size;
	u8 cec_buf[CEC_BUF_SIZE];
	struct cec_ops cops;
	struct cec_cbs ccbs;
	char disp_switch_name[MAX_SWITCH_NAME_SIZE];
	u32 current_vic;
};

static struct mdss_dba_utils_data *mdss_dba_utils_get_data(
	struct device *device)
{
	struct msm_fb_data_type *mfd;
	struct mdss_panel_info *pinfo;
	struct fb_info *fbi;
	struct mdss_dba_utils_data *udata = NULL;

	if (!device) {
		pr_err("Invalid device data\n");
		goto end;
	}

	fbi = dev_get_drvdata(device);
	if (!fbi) {
		pr_err("Invalid fbi data\n");
		goto end;
	}

	mfd = (struct msm_fb_data_type *)fbi->par;
	if (!mfd) {
		pr_err("Invalid mfd data\n");
		goto end;
	}

	pinfo = mfd->panel_info;
	if (!pinfo) {
		pr_err("Invalid pinfo data\n");
		goto end;
	}

	udata = pinfo->dba_data;
end:
	return udata;
}

static void mdss_dba_utils_send_display_notification(
	struct mdss_dba_utils_data *udata, int val)
{
	int state = 0;

	if (!udata) {
		pr_err("invalid input\n");
		return;
	}

	if (!udata->display_switch_registered) {
		pr_err("display switch not registered\n");
		return;
	}

	state = udata->sdev_display.state;

	switch_set_state(&udata->sdev_display, val);

	pr_debug("cable state %s %d\n",
		udata->sdev_display.state == state ?
		"is same" : "switched to",
		udata->sdev_display.state);
}

static void mdss_dba_utils_send_audio_notification(
	struct mdss_dba_utils_data *udata, int val)
{
	int state = 0;

	if (!udata) {
		pr_err("invalid input\n");
		return;
	}

	if (!udata->audio_switch_registered) {
		pr_err("audio switch not registered\n");
		return;
	}

	state = udata->sdev_audio.state;

	switch_set_state(&udata->sdev_audio, val);

	pr_debug("audio state %s %d\n",
		udata->sdev_audio.state == state ?
		"is same" : "switched to",
		udata->sdev_audio.state);
}

static ssize_t mdss_dba_utils_sysfs_rda_connected(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	struct mdss_dba_utils_data *udata = NULL;

	if (!dev) {
		pr_err("invalid device\n");
		return -EINVAL;
	}

	udata = mdss_dba_utils_get_data(dev);

	if (!udata) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	ret = snprintf(buf, PAGE_SIZE, "%d\n", udata->hpd_state);
	pr_debug("'%d'\n", udata->hpd_state);

	return ret;
}

static ssize_t mdss_dba_utils_sysfs_rda_video_mode(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	struct mdss_dba_utils_data *udata = NULL;

	if (!dev) {
		pr_debug("invalid device\n");
		return -EINVAL;
	}

	udata = mdss_dba_utils_get_data(dev);

	if (!udata) {
		pr_debug("invalid input\n");
		return -EINVAL;
	}

	ret = snprintf(buf, PAGE_SIZE, "%d\n", udata->current_vic);
	pr_debug("'%d'\n", udata->current_vic);

	return ret;
}

static ssize_t mdss_dba_utils_sysfs_wta_hpd(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct mdss_dba_utils_data *udata = NULL;
	int rc, hpd;

	udata = mdss_dba_utils_get_data(dev);
	if (!udata) {
		pr_debug("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	rc = kstrtoint(buf, 10, &hpd);
	if (rc) {
		pr_debug("%s: kstrtoint failed\n", __func__);
		return -EINVAL;
	}

	pr_debug("%s: set value: %d hpd state: %d\n", __func__,
					hpd, udata->hpd_state);
	if (!hpd) {
		if (udata->ops.power_on)
			udata->ops.power_on(udata->dba_data, false, 0);
		return count;
	}

	/* power on downstream device */
	if (udata->ops.power_on)
		udata->ops.power_on(udata->dba_data, true, 0);

	/* check if cable is connected to bridge chip */
	if (udata->ops.check_hpd)
		udata->ops.check_hpd(udata->dba_data, 0);

	return count;
}

static DEVICE_ATTR(connected, S_IRUGO,
		mdss_dba_utils_sysfs_rda_connected, NULL);

static DEVICE_ATTR(video_mode, S_IRUGO,
		mdss_dba_utils_sysfs_rda_video_mode, NULL);

static DEVICE_ATTR(hpd, S_IRUGO | S_IWUSR, NULL,
		mdss_dba_utils_sysfs_wta_hpd);

static struct attribute *mdss_dba_utils_fs_attrs[] = {
	&dev_attr_connected.attr,
	&dev_attr_video_mode.attr,
	&dev_attr_hpd.attr,
	NULL,
};

static struct attribute_group mdss_dba_utils_fs_attrs_group = {
	.attrs = mdss_dba_utils_fs_attrs,
};

static int mdss_dba_utils_sysfs_create(struct kobject *kobj)
{
	int rc;

	if (!kobj) {
		pr_err("invalid input\n");
		return -ENODEV;
	}

	rc = sysfs_create_group(kobj, &mdss_dba_utils_fs_attrs_group);
	if (rc) {
		pr_err("failed, rc=%d\n", rc);
		return rc;
	}

	return 0;
}

static void mdss_dba_utils_sysfs_remove(struct kobject *kobj)
{
	if (!kobj) {
		pr_err("invalid input\n");
		return;
	}

	sysfs_remove_group(kobj, &mdss_dba_utils_fs_attrs_group);
}

static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
{
	int ret = -EINVAL;
	struct mdss_dba_utils_data *udata = data;
	struct cec_msg msg = {0};
	bool pluggable = false;
	bool operands_present = false;
	u32 no_of_operands, size, i;
	u32 operands_offset = MAX_CEC_FRAME_SIZE - MAX_OPERAND_SIZE;

	if (!udata) {
		pr_err("Invalid data\n");
		return;
	}

	pr_debug("event: %d\n", event);

	if (udata->pinfo)
		pluggable = udata->pinfo->is_pluggable;

	switch (event) {
	case MSM_DBA_CB_HPD_CONNECT:
		if (udata->hpd_state)
			break;
		if (udata->ops.get_raw_edid) {
			ret = udata->ops.get_raw_edid(udata->dba_data,
				udata->edid_buf_size, udata->edid_buf, 0);

			if (!ret)
				hdmi_edid_parser(udata->edid_data);
			else
				pr_err("failed to get edid%d\n", ret);
		}

		if (pluggable) {
			mdss_dba_utils_send_display_notification(udata, 1);
			mdss_dba_utils_send_audio_notification(udata, 1);
		} else {
			mdss_dba_utils_video_on(udata, udata->pinfo);
		}

		udata->hpd_state = true;
		break;

	case MSM_DBA_CB_HPD_DISCONNECT:
		if (!udata->hpd_state)
			break;
		if (pluggable) {
			mdss_dba_utils_send_audio_notification(udata, 0);
			mdss_dba_utils_send_display_notification(udata, 0);
		} else {
			mdss_dba_utils_video_off(udata);
		}

		udata->hpd_state = false;
		break;

	case MSM_DBA_CB_CEC_READ_PENDING:
		if (udata->ops.hdmi_cec_read) {
			ret = udata->ops.hdmi_cec_read(
				udata->dba_data,
				&size,
				udata->cec_buf, 0);

			if (ret || !size || size > CEC_BUF_SIZE) {
				pr_err("%s: cec read failed\n", __func__);
				return;
			}
		}

		/* prepare cec msg */
		msg.recvr_id   = udata->cec_buf[0] & 0x0F;
		msg.sender_id  = (udata->cec_buf[0] & 0xF0) >> 4;
		msg.opcode     = udata->cec_buf[1];
		msg.frame_size = (udata->cec_buf[MAX_CEC_FRAME_SIZE] & 0x1F);

		operands_present = (msg.frame_size > operands_offset) &&
			(msg.frame_size <= MAX_CEC_FRAME_SIZE);

		if (operands_present) {
			no_of_operands = msg.frame_size - operands_offset;

			for (i = 0; i < no_of_operands; i++)
				msg.operand[i] =
					udata->cec_buf[operands_offset + i];
		}

		ret = udata->ccbs.msg_recv_notify(udata->ccbs.data, &msg);
		if (ret)
			pr_err("%s: failed to notify cec msg\n", __func__);
		break;

	default:
		break;
	}
}

static int mdss_dba_utils_cec_enable(void *data, bool enable)
{
	int ret = -EINVAL;
	struct mdss_dba_utils_data *udata = data;

	if (!udata) {
		pr_err("%s: Invalid data\n", __func__);
		return -EINVAL;
	}

	if (udata->ops.hdmi_cec_on)
		ret = udata->ops.hdmi_cec_on(udata->dba_data, enable, 0);

	return ret;
}

static int mdss_dba_utils_send_cec_msg(void *data, struct cec_msg *msg)
{
	int ret = -EINVAL, i;
	u32 operands_offset = MAX_CEC_FRAME_SIZE - MAX_OPERAND_SIZE;
	struct mdss_dba_utils_data *udata = data;

	u8 buf[MAX_CEC_FRAME_SIZE];

	if (!udata || !msg) {
		pr_err("%s: Invalid data\n", __func__);
		return -EINVAL;
	}

	buf[0] = (msg->sender_id << 4) | msg->recvr_id;
	buf[1] = msg->opcode;

	for (i = 0; i < MAX_OPERAND_SIZE &&
		i < msg->frame_size - operands_offset; i++)
		buf[operands_offset + i] = msg->operand[i];

	if (udata->ops.hdmi_cec_write)
		ret = udata->ops.hdmi_cec_write(udata->dba_data,
			msg->frame_size, (char *)buf, 0);

	return ret;
}

static int mdss_dba_utils_init_switch_dev(struct mdss_dba_utils_data *udata,
	u32 fb_node)
{
	int rc = -EINVAL, ret;

	if (!udata) {
		pr_err("invalid input\n");
		goto end;
	}

	/* create switch device to update display modules */
	udata->sdev_display.name = "hdmi";
	rc = switch_dev_register(&udata->sdev_display);
	if (rc) {
		pr_err("display switch registration failed\n");
		goto end;
	}

	udata->display_switch_registered = true;

	/* create switch device to update audio modules */
	udata->sdev_audio.name = "hdmi_audio";
	ret = switch_dev_register(&udata->sdev_audio);
	if (ret) {
		pr_err("audio switch registration failed\n");
		goto end;
	}

	udata->audio_switch_registered = true;
end:
	return rc;
}

static int mdss_dba_get_vic_panel_info(struct mdss_dba_utils_data *udata,
					struct mdss_panel_info *pinfo)
{
	struct msm_hdmi_mode_timing_info timing;
	struct hdmi_util_ds_data ds_data;
	u32 h_total, v_total, vic = 0;

	if (!udata || !pinfo) {
		pr_err("%s: invalid input\n", __func__);
		return 0;
	}

	timing.active_h = pinfo->xres;
	timing.back_porch_h = pinfo->lcdc.h_back_porch;
	timing.front_porch_h = pinfo->lcdc.h_front_porch;
	timing.pulse_width_h = pinfo->lcdc.h_pulse_width;
	h_total = (timing.active_h + timing.back_porch_h +
		timing.front_porch_h + timing.pulse_width_h);

	timing.active_v = pinfo->yres;
	timing.back_porch_v = pinfo->lcdc.v_back_porch;
	timing.front_porch_v = pinfo->lcdc.v_front_porch;
	timing.pulse_width_v = pinfo->lcdc.v_pulse_width;
	v_total = (timing.active_v + timing.back_porch_v +
		timing.front_porch_v + timing.pulse_width_v);

	timing.refresh_rate = pinfo->mipi.frame_rate * 1000;
	timing.pixel_freq = (h_total * v_total *
				pinfo->mipi.frame_rate) / 1000;

	ds_data.ds_registered = true;
	ds_data.ds_max_clk = MSM_DBA_MAX_PCLK;

	vic = hdmi_get_video_id_code(&timing, &ds_data);
	pr_debug("%s: current vic code is %d\n", __func__, vic);

	return vic;
}

/**
 * mdss_dba_utils_video_on() - Allow clients to switch on the video
 * @data: DBA utils instance which was allocated during registration
 * @pinfo: detailed panel information like x, y, porch values etc
 *
 * This API is used to power on the video on device registered
 * with DBA.
 *
 * Return: returns the result of the video on call on device.
 */
int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo)
{
	struct mdss_dba_utils_data *ud = data;
	struct msm_dba_video_cfg video_cfg;
	int ret = -EINVAL;

	if (!ud || !pinfo) {
		pr_err("invalid input\n");
		goto end;
	}

	memset(&video_cfg, 0, sizeof(video_cfg));

	video_cfg.h_active = pinfo->xres;
	video_cfg.v_active = pinfo->yres;
	video_cfg.h_front_porch = pinfo->lcdc.h_front_porch;
	video_cfg.v_front_porch = pinfo->lcdc.v_front_porch;
	video_cfg.h_back_porch = pinfo->lcdc.h_back_porch;
	video_cfg.v_back_porch = pinfo->lcdc.v_back_porch;
	video_cfg.h_pulse_width = pinfo->lcdc.h_pulse_width;
	video_cfg.v_pulse_width = pinfo->lcdc.v_pulse_width;
	video_cfg.pclk_khz = (unsigned long)pinfo->clk_rate / 1000;
	video_cfg.hdmi_mode = hdmi_edid_get_sink_mode(ud->edid_data);

	/* Calculate number of DSI lanes configured */
	video_cfg.num_of_input_lanes = 0;
	if (pinfo->mipi.data_lane0)
		video_cfg.num_of_input_lanes++;
	if (pinfo->mipi.data_lane1)
		video_cfg.num_of_input_lanes++;
	if (pinfo->mipi.data_lane2)
		video_cfg.num_of_input_lanes++;
	if (pinfo->mipi.data_lane3)
		video_cfg.num_of_input_lanes++;

	/* Get scan information from EDID */
	video_cfg.vic = mdss_dba_get_vic_panel_info(ud, pinfo);
	ud->current_vic = video_cfg.vic;
	video_cfg.scaninfo = hdmi_edid_get_sink_scaninfo(ud->edid_data,
							video_cfg.vic);
	if (ud->ops.video_on)
		ret = ud->ops.video_on(ud->dba_data, true, &video_cfg, 0);

end:
	return ret;
}

/**
 * mdss_dba_utils_video_off() - Allow clients to switch off the video
 * @data: DBA utils instance which was allocated during registration
 *
 * This API is used to power off the video on device registered
 * with DBA.
 *
 * Return: returns the result of the video off call on device.
 */
int mdss_dba_utils_video_off(void *data)
{
	struct mdss_dba_utils_data *ud = data;
	int ret = -EINVAL;

	if (!ud) {
		pr_err("invalid input\n");
		goto end;
	}

	if (ud->ops.video_on)
		ret = ud->ops.video_on(ud->dba_data, false, NULL, 0);

end:
	return ret;
}

/**
 * mdss_dba_utils_hdcp_enable() - Allow clients to switch on HDCP.
 * @data: DBA utils instance which was allocated during registration
 * @enable: flag to enable or disable HDCP authentication
 *
 * This API is used to start the HDCP authentication process with the
 * device registered with DBA.
 */
void mdss_dba_utils_hdcp_enable(void *data, bool enable)
{
	struct mdss_dba_utils_data *ud = data;

	if (!ud) {
		pr_err("invalid input\n");
		return;
	}

	if (ud->ops.hdcp_enable)
		ud->ops.hdcp_enable(ud->dba_data, enable, enable, 0);
}

/**
 * mdss_dba_utils_init() - Allow clients to register with DBA utils
 * @uid: Initialization data for registration.
 *
 * This API lets the client to register with DBA Utils module.
 * This allocate utils' instance and register with DBA (Display
 * Bridge Abstract). Creates sysfs nodes and switch nodes to interact
 * with other modules. Also registers with EDID parser to parse
 * the EDID buffer.
 *
 * Return: Instance of DBA utils which needs to be sent as parameter
 * when calling DBA utils APIs.
 */
void *mdss_dba_utils_init(struct mdss_dba_utils_init_data *uid)
{
	struct hdmi_edid_init_data edid_init_data;
	struct mdss_dba_utils_data *udata = NULL;
	struct msm_dba_reg_info info;
	struct cec_abstract_init_data cec_abst_init_data;
	void *cec_abst_data;
	int ret = 0;

	if (!uid) {
		pr_err("invalid input\n");
		ret = -EINVAL;
		goto error;
	}

	udata = kzalloc(sizeof(*udata), GFP_KERNEL);
	if (!udata) {
		ret = -ENOMEM;
		goto error;
	}

	memset(&edid_init_data, 0, sizeof(edid_init_data));
	memset(&info, 0, sizeof(info));

	/* initialize DBA registration data */
	strlcpy(info.client_name, uid->client_name, MSM_DBA_CLIENT_NAME_LEN);
	strlcpy(info.chip_name, uid->chip_name, MSM_DBA_CHIP_NAME_MAX_LEN);
	info.instance_id = uid->instance_id;
	info.cb = mdss_dba_utils_dba_cb;
	info.cb_data = udata;

	/* register client with DBA and get device's ops*/
	if (IS_ENABLED(CONFIG_MSM_DBA)) {
		udata->dba_data = msm_dba_register_client(&info, &udata->ops);
		if (IS_ERR_OR_NULL(udata->dba_data)) {
			pr_err("ds not configured\n");
			ret = PTR_ERR(udata->dba_data);
			goto error;
		}
	} else {
		pr_err("DBA not enabled\n");
		ret = -ENODEV;
		goto error;
	}

	/* create sysfs nodes for other modules to intract with utils */
	ret = mdss_dba_utils_sysfs_create(uid->kobj);
	if (ret) {
		pr_err("sysfs creation failed\n");
		goto error;
	}

	/* keep init data for future use */
	udata->kobj = uid->kobj;
	udata->pinfo = uid->pinfo;

	/* register display and audio switch devices */
	ret = mdss_dba_utils_init_switch_dev(udata, uid->fb_node);
	if (ret) {
		pr_err("switch dev registration failed\n");
		goto error;
	}

	/* Initialize EDID feature */
	edid_init_data.kobj = uid->kobj;
	edid_init_data.ds_data.ds_registered = true;
	edid_init_data.ds_data.ds_max_clk = MSM_DBA_MAX_PCLK;
	edid_init_data.max_pclk_khz = MSM_DBA_MAX_PCLK;

	/* register with edid module for parsing edid buffer */
	udata->edid_data = hdmi_edid_init(&edid_init_data);
	if (!udata->edid_data) {
		pr_err("edid parser init failed\n");
		ret = -ENODEV;
		goto error;
	}

	/* update edid data to retrieve it back in edid parser */
	if (uid->pinfo)
		uid->pinfo->edid_data = udata->edid_data;

	/* get edid buffer from edid parser */
	udata->edid_buf = edid_init_data.buf;
	udata->edid_buf_size = edid_init_data.buf_size;

	/* Initialize cec abstract layer and get callbacks */
	udata->cops.send_msg = mdss_dba_utils_send_cec_msg;
	udata->cops.enable   = mdss_dba_utils_cec_enable;
	udata->cops.data     = udata;

	/* initialize cec abstraction module */
	cec_abst_init_data.kobj = uid->kobj;
	cec_abst_init_data.ops  = &udata->cops;
	cec_abst_init_data.cbs  = &udata->ccbs;

	udata->cec_abst_data = cec_abstract_init(&cec_abst_init_data);
	if (IS_ERR_OR_NULL(udata->cec_abst_data)) {
		pr_err("error initializing cec abstract module\n");
		ret = PTR_ERR(cec_abst_data);
		goto error;
	}

	/* update cec data to retrieve it back in cec abstract module */
	if (uid->pinfo) {
		uid->pinfo->is_cec_supported = true;
		uid->pinfo->cec_data = udata->cec_abst_data;

		/*
		 * TODO: Currently there is no support from HAL to send
		 * HPD events to driver for usecase where bridge chip
		 * is used as primary panel. Once support is added remove
		 * this explicit calls to bridge chip driver.
		 */
		if (!uid->pinfo->is_pluggable) {
			if (udata->ops.power_on)
				udata->ops.power_on(udata->dba_data, true, 0);
			if (udata->ops.check_hpd)
				udata->ops.check_hpd(udata->dba_data, 0);
		}
	}

	return udata;

error:
	mdss_dba_utils_deinit(udata);
	return ERR_PTR(ret);
}

/**
 * mdss_dba_utils_deinit() - Allow clients to de-register with DBA utils
 * @data: DBA utils data that was allocated during registration.
 *
 * This API will release all the resources allocated during registration
 * and delete the DBA utils instance.
 */
void mdss_dba_utils_deinit(void *data)
{
	struct mdss_dba_utils_data *udata = data;

	if (!udata) {
		pr_err("invalid input\n");
		return;
	}

	if (!IS_ERR_OR_NULL(udata->cec_abst_data))
		cec_abstract_deinit(udata->cec_abst_data);

	if (udata->edid_data)
		hdmi_edid_deinit(udata->edid_data);

	if (udata->pinfo) {
		udata->pinfo->edid_data = NULL;
		udata->pinfo->is_cec_supported = false;
	}

	if (udata->audio_switch_registered)
		switch_dev_unregister(&udata->sdev_audio);

	if (udata->display_switch_registered)
		switch_dev_unregister(&udata->sdev_display);

	if (udata->kobj)
		mdss_dba_utils_sysfs_remove(udata->kobj);

	if (IS_ENABLED(CONFIG_MSM_DBA)) {
		if (!IS_ERR_OR_NULL(udata->dba_data))
			msm_dba_deregister_client(udata->dba_data);
	}

	kfree(udata);
}
+47 −0
Original line number Diff line number Diff line
/* 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 __MDSS_DBA_UTILS__
#define __MDSS_DBA_UTILS__

#include <linux/types.h>

#include "mdss_panel.h"

/**
 * struct mdss_dba_utils_init_data - Init data for registering with DBA utils.
 * @kobj: An instance of Kobject for sysfs creation
 * @instance_id: Instance ID of device registered with DBA
 * @chip_name: Name of the device registered with DBA
 * @client_name: Name of the client registering with DBA
 * @pinfo: Detailed panel information
 *
 * This structure's instance is needed to be passed as parameter
 * to register API to let the DBA utils module configure and
 * allocate an instance of DBA utils for the client.
 */
struct mdss_dba_utils_init_data {
	struct kobject *kobj;
	u32 instance_id;
	u32 fb_node;
	char *chip_name;
	char *client_name;
	struct mdss_panel_info *pinfo;
};

int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo);
int mdss_dba_utils_video_off(void *data);
void mdss_dba_utils_hdcp_enable(void *data, bool enable);

void *mdss_dba_utils_init(struct mdss_dba_utils_init_data *init_data);
void mdss_dba_utils_deinit(void *data);
#endif /* __MDSS_DBA_UTILS__ */
Loading