diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss')
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/dpi.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/dsi.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi.h | 7 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4.c | 62 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c | 353 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h | 55 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4_core.c | 30 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4_core.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 | 
11 files changed, 500 insertions, 31 deletions
| diff --git a/drivers/gpu/drm/omapdrm/dss/Kconfig b/drivers/gpu/drm/omapdrm/dss/Kconfig index 8b87d5cf45fc..f24ebf7f61dd 100644 --- a/drivers/gpu/drm/omapdrm/dss/Kconfig +++ b/drivers/gpu/drm/omapdrm/dss/Kconfig @@ -65,6 +65,14 @@ config OMAP4_DSS_HDMI  	help  	  HDMI support for OMAP4 based SoCs. +config OMAP4_DSS_HDMI_CEC +	bool "Enable HDMI CEC support for OMAP4" +	depends on OMAP4_DSS_HDMI +	select CEC_CORE +	default y +	---help--- +	  When selected the HDMI transmitter will support the CEC feature. +  config OMAP5_DSS_HDMI  	bool "HDMI support for OMAP5"  	default n diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile b/drivers/gpu/drm/omapdrm/dss/Makefile index 62d5b4f45420..904101c5e79d 100644 --- a/drivers/gpu/drm/omapdrm/dss/Makefile +++ b/drivers/gpu/drm/omapdrm/dss/Makefile @@ -15,5 +15,6 @@ omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o  omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \  	hdmi_phy.o  omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o +omapdss-$(CONFIG_OMAP4_DSS_HDMI_CEC) += hdmi4_cec.o  omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o  ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index daf286fc8a40..ca1e3b489540 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -566,8 +566,8 @@ static int dpi_verify_pll(struct dss_pll *pll)  }  static const struct soc_device_attribute dpi_soc_devices[] = { -	{ .family = "OMAP3[456]*" }, -	{ .family = "[AD]M37*" }, +	{ .machine = "OMAP3[456]*" }, +	{ .machine = "[AD]M37*" },  	{ /* sentinel */ }  }; diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index b56a05730314..c2cf6d98e577 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -4095,7 +4095,7 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev)  }  #ifdef DSI_CATCH_MISSING_TE -static void dsi_te_timeout(unsigned long arg) +static void dsi_te_timeout(struct timer_list *unused)  {  	DSSERR("TE not received for 250ms!\n");  } @@ -5449,9 +5449,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)  			     dsi_framedone_timeout_work_callback);  #ifdef DSI_CATCH_MISSING_TE -	init_timer(&dsi->te_timer); -	dsi->te_timer.function = dsi_te_timeout; -	dsi->te_timer.data = 0; +	timer_setup(&dsi->te_timer, dsi_te_timeout, 0);  #endif  	dsi_mem = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h index a820b394af09..c2609c448ddc 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -24,6 +24,7 @@  #include <linux/platform_device.h>  #include <linux/hdmi.h>  #include <sound/omap-hdmi-audio.h> +#include <media/cec.h>  #include "omapdss.h"  #include "dss.h" @@ -264,6 +265,10 @@ struct hdmi_core_data {  	void __iomem *base;  	bool cts_swmode;  	bool audio_use_mclk; + +	struct hdmi_wp_data *wp; +	unsigned int core_pwr_cnt; +	struct cec_adapter *adap;  };  static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx, @@ -373,7 +378,7 @@ struct omap_hdmi {  	bool audio_configured;  	struct omap_dss_audio audio_config; -	/* This lock should be taken when booleans bellow are touched. */ +	/* This lock should be taken when booleans below are touched. */  	spinlock_t audio_playing_lock;  	bool audio_playing;  	bool display_enabled; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index f169348da377..a598dfdeb585 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -36,9 +36,11 @@  #include <linux/of.h>  #include <linux/of_graph.h>  #include <sound/omap-hdmi-audio.h> +#include <media/cec.h>  #include "omapdss.h"  #include "hdmi4_core.h" +#include "hdmi4_cec.h"  #include "dss.h"  #include "hdmi.h" @@ -70,7 +72,8 @@ static void hdmi_runtime_put(void)  static irqreturn_t hdmi_irq_handler(int irq, void *data)  { -	struct hdmi_wp_data *wp = data; +	struct omap_hdmi *hdmi = data; +	struct hdmi_wp_data *wp = &hdmi->wp;  	u32 irqstatus;  	irqstatus = hdmi_wp_get_irqstatus(wp); @@ -95,6 +98,13 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)  	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {  		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);  	} +	if (irqstatus & HDMI_IRQ_CORE) { +		u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4); + +		hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4); +		if (intr4 & 8) +			hdmi4_cec_irq(&hdmi->core); +	}  	return IRQ_HANDLED;  } @@ -123,14 +133,19 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)  {  	int r; +	if (hdmi.core.core_pwr_cnt++) +		return 0; +  	r = regulator_enable(hdmi.vdda_reg);  	if (r) -		return r; +		goto err_reg_enable;  	r = hdmi_runtime_get();  	if (r)  		goto err_runtime_get; +	hdmi4_core_powerdown_disable(&hdmi.core); +  	/* Make selection of HDMI in DSS */  	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); @@ -140,12 +155,17 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)  err_runtime_get:  	regulator_disable(hdmi.vdda_reg); +err_reg_enable: +	hdmi.core.core_pwr_cnt--;  	return r;  }  static void hdmi_power_off_core(struct omap_dss_device *dssdev)  { +	if (--hdmi.core.core_pwr_cnt) +		return; +  	hdmi.core_enabled = false;  	hdmi_runtime_put(); @@ -166,8 +186,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)  		return r;  	/* disable and clear irqs */ -	hdmi_wp_clear_irqenable(wp, 0xffffffff); -	hdmi_wp_set_irqstatus(wp, 0xffffffff); +	hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE); +	hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE);  	vm = &hdmi.cfg.vm; @@ -242,7 +262,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)  {  	enum omap_channel channel = dssdev->dispc_channel; -	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); +	hdmi_wp_clear_irqenable(&hdmi.wp, ~HDMI_IRQ_CORE);  	hdmi_wp_video_stop(&hdmi.wp); @@ -393,11 +413,11 @@ static void hdmi_display_disable(struct omap_dss_device *dssdev)  	mutex_unlock(&hdmi.lock);  } -static int hdmi_core_enable(struct omap_dss_device *dssdev) +int hdmi4_core_enable(struct omap_dss_device *dssdev)  {  	int r = 0; -	DSSDBG("ENTER omapdss_hdmi_core_enable\n"); +	DSSDBG("ENTER omapdss_hdmi4_core_enable\n");  	mutex_lock(&hdmi.lock); @@ -415,9 +435,9 @@ err0:  	return r;  } -static void hdmi_core_disable(struct omap_dss_device *dssdev) +void hdmi4_core_disable(struct omap_dss_device *dssdev)  { -	DSSDBG("Enter omapdss_hdmi_core_disable\n"); +	DSSDBG("Enter omapdss_hdmi4_core_disable\n");  	mutex_lock(&hdmi.lock); @@ -475,19 +495,28 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev,  	need_enable = hdmi.core_enabled == false;  	if (need_enable) { -		r = hdmi_core_enable(dssdev); +		r = hdmi4_core_enable(dssdev);  		if (r)  			return r;  	}  	r = read_edid(edid, len); - +	if (r >= 256) +		hdmi4_cec_set_phys_addr(&hdmi.core, +					cec_get_edid_phys_addr(edid, r, NULL)); +	else +		hdmi4_cec_set_phys_addr(&hdmi.core, CEC_PHYS_ADDR_INVALID);  	if (need_enable) -		hdmi_core_disable(dssdev); +		hdmi4_core_disable(dssdev);  	return r;  } +static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) +{ +	hdmi4_cec_set_phys_addr(&hdmi.core, CEC_PHYS_ADDR_INVALID); +} +  static int hdmi_set_infoframe(struct omap_dss_device *dssdev,  		const struct hdmi_avi_infoframe *avi)  { @@ -514,6 +543,7 @@ static const struct omapdss_hdmi_ops hdmi_ops = {  	.get_timings		= hdmi_display_get_timings,  	.read_edid		= hdmi_read_edid, +	.lost_hotplug		= hdmi_lost_hotplug,  	.set_infoframe		= hdmi_set_infoframe,  	.set_hdmi_mode		= hdmi_set_hdmi_mode,  }; @@ -715,6 +745,10 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)  	if (r)  		goto err; +	r = hdmi4_cec_init(pdev, &hdmi.core, &hdmi.wp); +	if (r) +		goto err; +  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		DSSERR("platform_get_irq failed\n"); @@ -724,7 +758,7 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)  	r = devm_request_threaded_irq(&pdev->dev, irq,  			NULL, hdmi_irq_handler, -			IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); +			IRQF_ONESHOT, "OMAP HDMI", &hdmi);  	if (r) {  		DSSERR("HDMI IRQ request failed\n");  		goto err; @@ -759,6 +793,8 @@ static void hdmi4_unbind(struct device *dev, struct device *master, void *data)  	hdmi_uninit_output(pdev); +	hdmi4_cec_uninit(&hdmi.core); +  	hdmi_pll_uninit(&hdmi.pll);  	pm_runtime_disable(&pdev->dev); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c new file mode 100644 index 000000000000..23db74ae1826 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c @@ -0,0 +1,353 @@ +/* + * HDMI CEC + * + * Based on the CEC code from hdmi_ti_4xxx_ip.c from Android. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Yong Zhi + *	Mythri pk <[email protected]> + * + * Heavily modified to use the linux CEC framework: + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "dss.h" +#include "hdmi.h" +#include "hdmi4_core.h" +#include "hdmi4_cec.h" + +/* HDMI CEC */ +#define HDMI_CEC_DEV_ID                         0x900 +#define HDMI_CEC_SPEC                           0x904 + +/* Not really a debug register, more a low-level control register */ +#define HDMI_CEC_DBG_3                          0x91C +#define HDMI_CEC_TX_INIT                        0x920 +#define HDMI_CEC_TX_DEST                        0x924 +#define HDMI_CEC_SETUP                          0x938 +#define HDMI_CEC_TX_COMMAND                     0x93C +#define HDMI_CEC_TX_OPERAND                     0x940 +#define HDMI_CEC_TRANSMIT_DATA                  0x97C +#define HDMI_CEC_CA_7_0                         0x988 +#define HDMI_CEC_CA_15_8                        0x98C +#define HDMI_CEC_INT_STATUS_0                   0x998 +#define HDMI_CEC_INT_STATUS_1                   0x99C +#define HDMI_CEC_INT_ENABLE_0                   0x990 +#define HDMI_CEC_INT_ENABLE_1                   0x994 +#define HDMI_CEC_RX_CONTROL                     0x9B0 +#define HDMI_CEC_RX_COUNT                       0x9B4 +#define HDMI_CEC_RX_CMD_HEADER                  0x9B8 +#define HDMI_CEC_RX_COMMAND                     0x9BC +#define HDMI_CEC_RX_OPERAND                     0x9C0 + +#define HDMI_CEC_TX_FIFO_INT_MASK		0x64 +#define HDMI_CEC_RETRANSMIT_CNT_INT_MASK	0x2 + +#define HDMI_CORE_CEC_RETRY    200 + +static void hdmi_cec_received_msg(struct hdmi_core_data *core) +{ +	u32 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff; + +	/* While there are CEC frames in the FIFO */ +	while (cnt & 0x70) { +		/* and the frame doesn't have an error */ +		if (!(cnt & 0x80)) { +			struct cec_msg msg = {}; +			unsigned int i; + +			/* then read the message */ +			msg.len = cnt & 0xf; +			if (msg.len > CEC_MAX_MSG_SIZE - 2) +				msg.len = CEC_MAX_MSG_SIZE - 2; +			msg.msg[0] = hdmi_read_reg(core->base, +						   HDMI_CEC_RX_CMD_HEADER); +			msg.msg[1] = hdmi_read_reg(core->base, +						   HDMI_CEC_RX_COMMAND); +			for (i = 0; i < msg.len; i++) { +				unsigned int reg = HDMI_CEC_RX_OPERAND + i * 4; + +				msg.msg[2 + i] = +					hdmi_read_reg(core->base, reg); +			} +			msg.len += 2; +			cec_received_msg(core->adap, &msg); +		} +		/* Clear the current frame from the FIFO */ +		hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 1); +		/* Wait until the current frame is cleared */ +		while (hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL) & 1) +			udelay(1); +		/* +		 * Re-read the count register and loop to see if there are +		 * more messages in the FIFO. +		 */ +		cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff; +	} +} + +void hdmi4_cec_irq(struct hdmi_core_data *core) +{ +	u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0); +	u32 stat1 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1); + +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0); +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1); + +	if (stat0 & 0x20) { +		cec_transmit_done(core->adap, CEC_TX_STATUS_OK, +				  0, 0, 0, 0); +		REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); +	} else if (stat1 & 0x02) { +		u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); + +		cec_transmit_done(core->adap, +				  CEC_TX_STATUS_NACK | +				  CEC_TX_STATUS_MAX_RETRIES, +				  0, (dbg3 >> 4) & 7, 0, 0); +		REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); +	} +	if (stat0 & 0x02) +		hdmi_cec_received_msg(core); +} + +static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap) +{ +	struct hdmi_core_data *core = cec_get_drvdata(adap); +	int retry = HDMI_CORE_CEC_RETRY; +	int temp; + +	REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); +	while (retry) { +		temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); +		if (FLD_GET(temp, 7, 7) == 0) +			break; +		retry--; +	} +	return retry != 0; +} + +static bool hdmi_cec_clear_rx_fifo(struct cec_adapter *adap) +{ +	struct hdmi_core_data *core = cec_get_drvdata(adap); +	int retry = HDMI_CORE_CEC_RETRY; +	int temp; + +	hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 0x3); +	retry = HDMI_CORE_CEC_RETRY; +	while (retry) { +		temp = hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL); +		if (FLD_GET(temp, 1, 0) == 0) +			break; +		retry--; +	} +	return retry != 0; +} + +static int hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ +	struct hdmi_core_data *core = cec_get_drvdata(adap); +	int temp, err; + +	if (!enable) { +		hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0); +		hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0); +		REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0, 3, 3); +		hdmi_wp_clear_irqenable(core->wp, HDMI_IRQ_CORE); +		hdmi_wp_set_irqstatus(core->wp, HDMI_IRQ_CORE); +		hdmi4_core_disable(NULL); +		return 0; +	} +	err = hdmi4_core_enable(NULL); +	if (err) +		return err; + +	/* Clear TX FIFO */ +	if (!hdmi_cec_clear_tx_fifo(adap)) { +		pr_err("cec-%s: could not clear TX FIFO\n", adap->name); +		return -EIO; +	} + +	/* Clear RX FIFO */ +	if (!hdmi_cec_clear_rx_fifo(adap)) { +		pr_err("cec-%s: could not clear RX FIFO\n", adap->name); +		return -EIO; +	} + +	/* Clear CEC interrupts */ +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, +		hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1)); +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, +		hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0)); + +	/* Enable HDMI core interrupts */ +	hdmi_wp_set_irqenable(core->wp, HDMI_IRQ_CORE); +	/* Unmask CEC interrupt */ +	REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0x1, 3, 3); +	/* +	 * Enable CEC interrupts: +	 * Transmit Buffer Full/Empty Change event +	 * Receiver FIFO Not Empty event +	 */ +	hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22); +	/* +	 * Enable CEC interrupts: +	 * Frame Retransmit Count Exceeded event +	 */ +	hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02); + +	/* cec calibration enable (self clearing) */ +	hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03); +	msleep(20); +	hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x04); + +	temp = hdmi_read_reg(core->base, HDMI_CEC_SETUP); +	if (FLD_GET(temp, 4, 4) != 0) { +		temp = FLD_MOD(temp, 0, 4, 4); +		hdmi_write_reg(core->base, HDMI_CEC_SETUP, temp); + +		/* +		 * If we enabled CEC in middle of a CEC message on the bus, +		 * we could have start bit irregularity and/or short +		 * pulse event. Clear them now. +		 */ +		temp = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1); +		temp = FLD_MOD(0x0, 0x5, 2, 0); +		hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, temp); +	} +	return 0; +} + +static int hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ +	struct hdmi_core_data *core = cec_get_drvdata(adap); +	u32 v; + +	if (log_addr == CEC_LOG_ADDR_INVALID) { +		hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, 0); +		hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, 0); +		return 0; +	} +	if (log_addr <= 7) { +		v = hdmi_read_reg(core->base, HDMI_CEC_CA_7_0); +		v |= 1 << log_addr; +		hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, v); +	} else { +		v = hdmi_read_reg(core->base, HDMI_CEC_CA_15_8); +		v |= 1 << (log_addr - 8); +		hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, v); +	} +	return 0; +} + +static int hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, +				   u32 signal_free_time, struct cec_msg *msg) +{ +	struct hdmi_core_data *core = cec_get_drvdata(adap); +	int temp; +	u32 i; + +	/* Clear TX FIFO */ +	if (!hdmi_cec_clear_tx_fifo(adap)) { +		pr_err("cec-%s: could not clear TX FIFO for transmit\n", +		       adap->name); +		return -EIO; +	} + +	/* Clear TX interrupts */ +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, +		       HDMI_CEC_TX_FIFO_INT_MASK); + +	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, +		       HDMI_CEC_RETRANSMIT_CNT_INT_MASK); + +	/* Set the retry count */ +	REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, attempts - 1, 6, 4); + +	/* Set the initiator addresses */ +	hdmi_write_reg(core->base, HDMI_CEC_TX_INIT, cec_msg_initiator(msg)); + +	/* Set destination id */ +	temp = cec_msg_destination(msg); +	if (msg->len == 1) +		temp |= 0x80; +	hdmi_write_reg(core->base, HDMI_CEC_TX_DEST, temp); +	if (msg->len == 1) +		return 0; + +	/* Setup command and arguments for the command */ +	hdmi_write_reg(core->base, HDMI_CEC_TX_COMMAND, msg->msg[1]); + +	for (i = 0; i < msg->len - 2; i++) +		hdmi_write_reg(core->base, HDMI_CEC_TX_OPERAND + i * 4, +			       msg->msg[2 + i]); + +	/* Operand count */ +	hdmi_write_reg(core->base, HDMI_CEC_TRANSMIT_DATA, +		       (msg->len - 2) | 0x10); +	return 0; +} + +static const struct cec_adap_ops hdmi_cec_adap_ops = { +	.adap_enable = hdmi_cec_adap_enable, +	.adap_log_addr = hdmi_cec_adap_log_addr, +	.adap_transmit = hdmi_cec_adap_transmit, +}; + +void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa) +{ +	cec_s_phys_addr(core->adap, pa, false); +} + +int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core, +		  struct hdmi_wp_data *wp) +{ +	const u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | +			 CEC_CAP_PASSTHROUGH | CEC_CAP_RC; +	int ret; + +	core->adap = cec_allocate_adapter(&hdmi_cec_adap_ops, core, +		"omap4", caps, CEC_MAX_LOG_ADDRS); +	ret = PTR_ERR_OR_ZERO(core->adap); +	if (ret < 0) +		return ret; +	core->wp = wp; + +	/* +	 * Initialize CEC clock divider: CEC needs 2MHz clock hence +	 * set the devider to 24 to get 48/24=2MHz clock +	 */ +	REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0x18, 5, 0); + +	ret = cec_register_adapter(core->adap, &pdev->dev); +	if (ret < 0) { +		cec_delete_adapter(core->adap); +		return ret; +	} +	return 0; +} + +void hdmi4_cec_uninit(struct hdmi_core_data *core) +{ +	cec_unregister_adapter(core->adap); +} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h new file mode 100644 index 000000000000..0292337c97cc --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h @@ -0,0 +1,55 @@ +/* + * HDMI header definition for OMAP4 HDMI CEC IP + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _HDMI4_CEC_H_ +#define _HDMI4_CEC_H_ + +struct hdmi_core_data; +struct hdmi_wp_data; +struct platform_device; + +/* HDMI CEC funcs */ +#ifdef CONFIG_OMAP4_DSS_HDMI_CEC +void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa); +void hdmi4_cec_irq(struct hdmi_core_data *core); +int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core, +		  struct hdmi_wp_data *wp); +void hdmi4_cec_uninit(struct hdmi_core_data *core); +#else +static inline void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa) +{ +} + +static inline void hdmi4_cec_irq(struct hdmi_core_data *core) +{ +} + +static inline int hdmi4_cec_init(struct platform_device *pdev, +				struct hdmi_core_data *core, +				struct hdmi_wp_data *wp) +{ +	return 0; +} + +static inline void hdmi4_cec_uninit(struct hdmi_core_data *core) +{ +} +#endif + +#endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c index 365cf07daa01..b06f9956e733 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c @@ -208,9 +208,9 @@ static void hdmi_core_init(struct hdmi_core_video_config *video_cfg)  	video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK;  } -static void hdmi_core_powerdown_disable(struct hdmi_core_data *core) +void hdmi4_core_powerdown_disable(struct hdmi_core_data *core)  { -	DSSDBG("Enter hdmi_core_powerdown_disable\n"); +	DSSDBG("Enter hdmi4_core_powerdown_disable\n");  	REG_FLD_MOD(core->base, HDMI_CORE_SYS_SYS_CTRL1, 0x1, 0, 0);  } @@ -335,9 +335,6 @@ void hdmi4_configure(struct hdmi_core_data *core,  	 */  	hdmi_core_swreset_assert(core); -	/* power down off */ -	hdmi_core_powerdown_disable(core); -  	v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;  	v_core_cfg.hdmi_dvi = cfg->hdmi_dvi_mode; @@ -889,25 +886,36 @@ struct hdmi4_features {  	bool audio_use_mclk;  }; -static const struct hdmi4_features hdmi4_es1_features = { +static const struct hdmi4_features hdmi4430_es1_features = {  	.cts_swmode = false,  	.audio_use_mclk = false,  }; -static const struct hdmi4_features hdmi4_es2_features = { +static const struct hdmi4_features hdmi4430_es2_features = {  	.cts_swmode = true,  	.audio_use_mclk = false,  }; -static const struct hdmi4_features hdmi4_es3_features = { +static const struct hdmi4_features hdmi4_features = {  	.cts_swmode = true,  	.audio_use_mclk = true,  };  static const struct soc_device_attribute hdmi4_soc_devices[] = { -	{ .family = "OMAP4", .revision = "ES1.?", .data = &hdmi4_es1_features }, -	{ .family = "OMAP4", .revision = "ES2.?", .data = &hdmi4_es2_features }, -	{ .family = "OMAP4",			  .data = &hdmi4_es3_features }, +	{ +		.machine = "OMAP4430", +		.revision = "ES1.?", +		.data = &hdmi4430_es1_features, +	}, +	{ +		.machine = "OMAP4430", +		.revision = "ES2.?", +		.data = &hdmi4430_es2_features, +	}, +	{ +		.family = "OMAP4", +		.data = &hdmi4_features, +	},  	{ /* sentinel */ }  }; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h index a069f96ec6f6..b6ab579e44d2 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h @@ -266,6 +266,10 @@ void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,  void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s);  int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core); +int hdmi4_core_enable(struct omap_dss_device *dssdev); +void hdmi4_core_disable(struct omap_dss_device *dssdev); +void hdmi4_core_powerdown_disable(struct hdmi_core_data *core); +  int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp);  void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp);  int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 47a331670963..990422b35784 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -395,6 +395,7 @@ struct omapdss_hdmi_ops {  			    struct videomode *vm);  	int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); +	void (*lost_hotplug)(struct omap_dss_device *dssdev);  	bool (*detect)(struct omap_dss_device *dssdev);  	int (*register_hpd_cb)(struct omap_dss_device *dssdev, |