xref: /linux/drivers/media/cec/platform/stm32/stm32-cec.c (revision 6cb7d1b3ff83e98e852db9739892c4643a31804b)
14be5e864SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
24be5e864SMauro Carvalho Chehab /*
34be5e864SMauro Carvalho Chehab  * STM32 CEC driver
44be5e864SMauro Carvalho Chehab  * Copyright (C) STMicroelectronics SA 2017
54be5e864SMauro Carvalho Chehab  *
64be5e864SMauro Carvalho Chehab  */
74be5e864SMauro Carvalho Chehab 
84be5e864SMauro Carvalho Chehab #include <linux/clk.h>
94be5e864SMauro Carvalho Chehab #include <linux/interrupt.h>
104be5e864SMauro Carvalho Chehab #include <linux/kernel.h>
114be5e864SMauro Carvalho Chehab #include <linux/module.h>
124be5e864SMauro Carvalho Chehab #include <linux/of.h>
134be5e864SMauro Carvalho Chehab #include <linux/of_device.h>
144be5e864SMauro Carvalho Chehab #include <linux/platform_device.h>
154be5e864SMauro Carvalho Chehab #include <linux/regmap.h>
164be5e864SMauro Carvalho Chehab 
174be5e864SMauro Carvalho Chehab #include <media/cec.h>
184be5e864SMauro Carvalho Chehab 
194be5e864SMauro Carvalho Chehab #define CEC_NAME	"stm32-cec"
204be5e864SMauro Carvalho Chehab 
214be5e864SMauro Carvalho Chehab /* CEC registers  */
224be5e864SMauro Carvalho Chehab #define CEC_CR		0x0000 /* Control Register */
234be5e864SMauro Carvalho Chehab #define CEC_CFGR	0x0004 /* ConFiGuration Register */
244be5e864SMauro Carvalho Chehab #define CEC_TXDR	0x0008 /* Rx data Register */
254be5e864SMauro Carvalho Chehab #define CEC_RXDR	0x000C /* Rx data Register */
264be5e864SMauro Carvalho Chehab #define CEC_ISR		0x0010 /* Interrupt and status Register */
274be5e864SMauro Carvalho Chehab #define CEC_IER		0x0014 /* Interrupt enable Register */
284be5e864SMauro Carvalho Chehab 
294be5e864SMauro Carvalho Chehab #define TXEOM		BIT(2)
304be5e864SMauro Carvalho Chehab #define TXSOM		BIT(1)
314be5e864SMauro Carvalho Chehab #define CECEN		BIT(0)
324be5e864SMauro Carvalho Chehab 
334be5e864SMauro Carvalho Chehab #define LSTN		BIT(31)
344be5e864SMauro Carvalho Chehab #define OAR		GENMASK(30, 16)
354be5e864SMauro Carvalho Chehab #define SFTOP		BIT(8)
364be5e864SMauro Carvalho Chehab #define BRDNOGEN	BIT(7)
374be5e864SMauro Carvalho Chehab #define LBPEGEN		BIT(6)
384be5e864SMauro Carvalho Chehab #define BREGEN		BIT(5)
394be5e864SMauro Carvalho Chehab #define BRESTP		BIT(4)
404be5e864SMauro Carvalho Chehab #define RXTOL		BIT(3)
414be5e864SMauro Carvalho Chehab #define SFT		GENMASK(2, 0)
424be5e864SMauro Carvalho Chehab #define FULL_CFG	(LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \
434be5e864SMauro Carvalho Chehab 			 | RXTOL)
444be5e864SMauro Carvalho Chehab 
454be5e864SMauro Carvalho Chehab #define TXACKE		BIT(12)
464be5e864SMauro Carvalho Chehab #define TXERR		BIT(11)
474be5e864SMauro Carvalho Chehab #define TXUDR		BIT(10)
484be5e864SMauro Carvalho Chehab #define TXEND		BIT(9)
494be5e864SMauro Carvalho Chehab #define TXBR		BIT(8)
504be5e864SMauro Carvalho Chehab #define ARBLST		BIT(7)
514be5e864SMauro Carvalho Chehab #define RXACKE		BIT(6)
524be5e864SMauro Carvalho Chehab #define RXOVR		BIT(2)
534be5e864SMauro Carvalho Chehab #define RXEND		BIT(1)
544be5e864SMauro Carvalho Chehab #define RXBR		BIT(0)
554be5e864SMauro Carvalho Chehab 
564be5e864SMauro Carvalho Chehab #define ALL_TX_IT	(TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST)
574be5e864SMauro Carvalho Chehab #define ALL_RX_IT	(RXEND | RXBR | RXACKE | RXOVR)
584be5e864SMauro Carvalho Chehab 
594be5e864SMauro Carvalho Chehab /*
604be5e864SMauro Carvalho Chehab  * 400 ms is the time it takes for one 16 byte message to be
614be5e864SMauro Carvalho Chehab  * transferred and 5 is the maximum number of retries. Add
624be5e864SMauro Carvalho Chehab  * another 100 ms as a margin.
634be5e864SMauro Carvalho Chehab  */
644be5e864SMauro Carvalho Chehab #define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
654be5e864SMauro Carvalho Chehab 
664be5e864SMauro Carvalho Chehab struct stm32_cec {
674be5e864SMauro Carvalho Chehab 	struct cec_adapter	*adap;
684be5e864SMauro Carvalho Chehab 	struct device		*dev;
694be5e864SMauro Carvalho Chehab 	struct clk		*clk_cec;
704be5e864SMauro Carvalho Chehab 	struct clk		*clk_hdmi_cec;
714be5e864SMauro Carvalho Chehab 	struct reset_control	*rstc;
724be5e864SMauro Carvalho Chehab 	struct regmap		*regmap;
734be5e864SMauro Carvalho Chehab 	int			irq;
744be5e864SMauro Carvalho Chehab 	u32			irq_status;
754be5e864SMauro Carvalho Chehab 	struct cec_msg		rx_msg;
764be5e864SMauro Carvalho Chehab 	struct cec_msg		tx_msg;
774be5e864SMauro Carvalho Chehab 	int			tx_cnt;
784be5e864SMauro Carvalho Chehab };
794be5e864SMauro Carvalho Chehab 
804be5e864SMauro Carvalho Chehab static void cec_hw_init(struct stm32_cec *cec)
814be5e864SMauro Carvalho Chehab {
824be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0);
834be5e864SMauro Carvalho Chehab 
844be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT,
854be5e864SMauro Carvalho Chehab 			   ALL_TX_IT | ALL_RX_IT);
864be5e864SMauro Carvalho Chehab 
874be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG);
884be5e864SMauro Carvalho Chehab }
894be5e864SMauro Carvalho Chehab 
904be5e864SMauro Carvalho Chehab static void stm32_tx_done(struct stm32_cec *cec, u32 status)
914be5e864SMauro Carvalho Chehab {
924be5e864SMauro Carvalho Chehab 	if (status & (TXERR | TXUDR)) {
934be5e864SMauro Carvalho Chehab 		cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR,
944be5e864SMauro Carvalho Chehab 				  0, 0, 0, 1);
954be5e864SMauro Carvalho Chehab 		return;
964be5e864SMauro Carvalho Chehab 	}
974be5e864SMauro Carvalho Chehab 
984be5e864SMauro Carvalho Chehab 	if (status & ARBLST) {
994be5e864SMauro Carvalho Chehab 		cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST,
1004be5e864SMauro Carvalho Chehab 				  1, 0, 0, 0);
1014be5e864SMauro Carvalho Chehab 		return;
1024be5e864SMauro Carvalho Chehab 	}
1034be5e864SMauro Carvalho Chehab 
1044be5e864SMauro Carvalho Chehab 	if (status & TXACKE) {
1054be5e864SMauro Carvalho Chehab 		cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK,
1064be5e864SMauro Carvalho Chehab 				  0, 1, 0, 0);
1074be5e864SMauro Carvalho Chehab 		return;
1084be5e864SMauro Carvalho Chehab 	}
1094be5e864SMauro Carvalho Chehab 
1104be5e864SMauro Carvalho Chehab 	if (cec->irq_status & TXBR) {
1114be5e864SMauro Carvalho Chehab 		/* send next byte */
1124be5e864SMauro Carvalho Chehab 		if (cec->tx_cnt < cec->tx_msg.len)
1134be5e864SMauro Carvalho Chehab 			regmap_write(cec->regmap, CEC_TXDR,
1144be5e864SMauro Carvalho Chehab 				     cec->tx_msg.msg[cec->tx_cnt++]);
1154be5e864SMauro Carvalho Chehab 
1164be5e864SMauro Carvalho Chehab 		/* TXEOM is set to command transmission of the last byte */
1174be5e864SMauro Carvalho Chehab 		if (cec->tx_cnt == cec->tx_msg.len)
1184be5e864SMauro Carvalho Chehab 			regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
1194be5e864SMauro Carvalho Chehab 	}
1204be5e864SMauro Carvalho Chehab 
1214be5e864SMauro Carvalho Chehab 	if (cec->irq_status & TXEND)
1224be5e864SMauro Carvalho Chehab 		cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
1234be5e864SMauro Carvalho Chehab }
1244be5e864SMauro Carvalho Chehab 
1254be5e864SMauro Carvalho Chehab static void stm32_rx_done(struct stm32_cec *cec, u32 status)
1264be5e864SMauro Carvalho Chehab {
1274be5e864SMauro Carvalho Chehab 	if (cec->irq_status & (RXACKE | RXOVR)) {
1284be5e864SMauro Carvalho Chehab 		cec->rx_msg.len = 0;
1294be5e864SMauro Carvalho Chehab 		return;
1304be5e864SMauro Carvalho Chehab 	}
1314be5e864SMauro Carvalho Chehab 
1324be5e864SMauro Carvalho Chehab 	if (cec->irq_status & RXBR) {
1334be5e864SMauro Carvalho Chehab 		u32 val;
1344be5e864SMauro Carvalho Chehab 
1354be5e864SMauro Carvalho Chehab 		regmap_read(cec->regmap, CEC_RXDR, &val);
1364be5e864SMauro Carvalho Chehab 		cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
1374be5e864SMauro Carvalho Chehab 	}
1384be5e864SMauro Carvalho Chehab 
1394be5e864SMauro Carvalho Chehab 	if (cec->irq_status & RXEND) {
1404be5e864SMauro Carvalho Chehab 		cec_received_msg(cec->adap, &cec->rx_msg);
1414be5e864SMauro Carvalho Chehab 		cec->rx_msg.len = 0;
1424be5e864SMauro Carvalho Chehab 	}
1434be5e864SMauro Carvalho Chehab }
1444be5e864SMauro Carvalho Chehab 
1454be5e864SMauro Carvalho Chehab static irqreturn_t stm32_cec_irq_thread(int irq, void *arg)
1464be5e864SMauro Carvalho Chehab {
1474be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = arg;
1484be5e864SMauro Carvalho Chehab 
1494be5e864SMauro Carvalho Chehab 	if (cec->irq_status & ALL_TX_IT)
1504be5e864SMauro Carvalho Chehab 		stm32_tx_done(cec, cec->irq_status);
1514be5e864SMauro Carvalho Chehab 
1524be5e864SMauro Carvalho Chehab 	if (cec->irq_status & ALL_RX_IT)
1534be5e864SMauro Carvalho Chehab 		stm32_rx_done(cec, cec->irq_status);
1544be5e864SMauro Carvalho Chehab 
1554be5e864SMauro Carvalho Chehab 	cec->irq_status = 0;
1564be5e864SMauro Carvalho Chehab 
1574be5e864SMauro Carvalho Chehab 	return IRQ_HANDLED;
1584be5e864SMauro Carvalho Chehab }
1594be5e864SMauro Carvalho Chehab 
1604be5e864SMauro Carvalho Chehab static irqreturn_t stm32_cec_irq_handler(int irq, void *arg)
1614be5e864SMauro Carvalho Chehab {
1624be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = arg;
1634be5e864SMauro Carvalho Chehab 
1644be5e864SMauro Carvalho Chehab 	regmap_read(cec->regmap, CEC_ISR, &cec->irq_status);
1654be5e864SMauro Carvalho Chehab 
1664be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_ISR,
1674be5e864SMauro Carvalho Chehab 			   ALL_TX_IT | ALL_RX_IT,
1684be5e864SMauro Carvalho Chehab 			   ALL_TX_IT | ALL_RX_IT);
1694be5e864SMauro Carvalho Chehab 
1704be5e864SMauro Carvalho Chehab 	return IRQ_WAKE_THREAD;
1714be5e864SMauro Carvalho Chehab }
1724be5e864SMauro Carvalho Chehab 
1734be5e864SMauro Carvalho Chehab static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable)
1744be5e864SMauro Carvalho Chehab {
1754be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = adap->priv;
1764be5e864SMauro Carvalho Chehab 	int ret = 0;
1774be5e864SMauro Carvalho Chehab 
1784be5e864SMauro Carvalho Chehab 	if (enable) {
1794be5e864SMauro Carvalho Chehab 		ret = clk_enable(cec->clk_cec);
1804be5e864SMauro Carvalho Chehab 		if (ret)
1814be5e864SMauro Carvalho Chehab 			dev_err(cec->dev, "fail to enable cec clock\n");
1824be5e864SMauro Carvalho Chehab 
1834be5e864SMauro Carvalho Chehab 		clk_enable(cec->clk_hdmi_cec);
1844be5e864SMauro Carvalho Chehab 		regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
1854be5e864SMauro Carvalho Chehab 	} else {
1864be5e864SMauro Carvalho Chehab 		clk_disable(cec->clk_cec);
1874be5e864SMauro Carvalho Chehab 		clk_disable(cec->clk_hdmi_cec);
1884be5e864SMauro Carvalho Chehab 		regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
1894be5e864SMauro Carvalho Chehab 	}
1904be5e864SMauro Carvalho Chehab 
1914be5e864SMauro Carvalho Chehab 	return ret;
1924be5e864SMauro Carvalho Chehab }
1934be5e864SMauro Carvalho Chehab 
1944be5e864SMauro Carvalho Chehab static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
1954be5e864SMauro Carvalho Chehab {
1964be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = adap->priv;
1974be5e864SMauro Carvalho Chehab 	u32 oar = (1 << logical_addr) << 16;
1984be5e864SMauro Carvalho Chehab 	u32 val;
1994be5e864SMauro Carvalho Chehab 
2004be5e864SMauro Carvalho Chehab 	/* Poll every 100µs the register CEC_CR to wait end of transmission */
2014be5e864SMauro Carvalho Chehab 	regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM),
2024be5e864SMauro Carvalho Chehab 				 100, CEC_XFER_TIMEOUT_MS * 1000);
2034be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
2044be5e864SMauro Carvalho Chehab 
2054be5e864SMauro Carvalho Chehab 	if (logical_addr == CEC_LOG_ADDR_INVALID)
2064be5e864SMauro Carvalho Chehab 		regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0);
2074be5e864SMauro Carvalho Chehab 	else
2084be5e864SMauro Carvalho Chehab 		regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar);
2094be5e864SMauro Carvalho Chehab 
2104be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
2114be5e864SMauro Carvalho Chehab 
2124be5e864SMauro Carvalho Chehab 	return 0;
2134be5e864SMauro Carvalho Chehab }
2144be5e864SMauro Carvalho Chehab 
2154be5e864SMauro Carvalho Chehab static int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
2164be5e864SMauro Carvalho Chehab 				   u32 signal_free_time, struct cec_msg *msg)
2174be5e864SMauro Carvalho Chehab {
2184be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = adap->priv;
2194be5e864SMauro Carvalho Chehab 
2204be5e864SMauro Carvalho Chehab 	/* Copy message */
2214be5e864SMauro Carvalho Chehab 	cec->tx_msg = *msg;
2224be5e864SMauro Carvalho Chehab 	cec->tx_cnt = 0;
2234be5e864SMauro Carvalho Chehab 
2244be5e864SMauro Carvalho Chehab 	/*
2254be5e864SMauro Carvalho Chehab 	 * If the CEC message consists of only one byte,
2264be5e864SMauro Carvalho Chehab 	 * TXEOM must be set before of TXSOM.
2274be5e864SMauro Carvalho Chehab 	 */
2284be5e864SMauro Carvalho Chehab 	if (cec->tx_msg.len == 1)
2294be5e864SMauro Carvalho Chehab 		regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
2304be5e864SMauro Carvalho Chehab 
2314be5e864SMauro Carvalho Chehab 	/* TXSOM is set to command transmission of the first byte */
2324be5e864SMauro Carvalho Chehab 	regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM);
2334be5e864SMauro Carvalho Chehab 
2344be5e864SMauro Carvalho Chehab 	/* Write the header (first byte of message) */
2354be5e864SMauro Carvalho Chehab 	regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]);
2364be5e864SMauro Carvalho Chehab 	cec->tx_cnt++;
2374be5e864SMauro Carvalho Chehab 
2384be5e864SMauro Carvalho Chehab 	return 0;
2394be5e864SMauro Carvalho Chehab }
2404be5e864SMauro Carvalho Chehab 
2414be5e864SMauro Carvalho Chehab static const struct cec_adap_ops stm32_cec_adap_ops = {
2424be5e864SMauro Carvalho Chehab 	.adap_enable = stm32_cec_adap_enable,
2434be5e864SMauro Carvalho Chehab 	.adap_log_addr = stm32_cec_adap_log_addr,
2444be5e864SMauro Carvalho Chehab 	.adap_transmit = stm32_cec_adap_transmit,
2454be5e864SMauro Carvalho Chehab };
2464be5e864SMauro Carvalho Chehab 
2474be5e864SMauro Carvalho Chehab static const struct regmap_config stm32_cec_regmap_cfg = {
2484be5e864SMauro Carvalho Chehab 	.reg_bits = 32,
2494be5e864SMauro Carvalho Chehab 	.val_bits = 32,
2504be5e864SMauro Carvalho Chehab 	.reg_stride = sizeof(u32),
2514be5e864SMauro Carvalho Chehab 	.max_register = 0x14,
2524be5e864SMauro Carvalho Chehab 	.fast_io = true,
2534be5e864SMauro Carvalho Chehab };
2544be5e864SMauro Carvalho Chehab 
2554be5e864SMauro Carvalho Chehab static int stm32_cec_probe(struct platform_device *pdev)
2564be5e864SMauro Carvalho Chehab {
2574be5e864SMauro Carvalho Chehab 	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_MODE_MONITOR_ALL;
2584be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec;
2594be5e864SMauro Carvalho Chehab 	void __iomem *mmio;
2604be5e864SMauro Carvalho Chehab 	int ret;
2614be5e864SMauro Carvalho Chehab 
2624be5e864SMauro Carvalho Chehab 	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
2634be5e864SMauro Carvalho Chehab 	if (!cec)
2644be5e864SMauro Carvalho Chehab 		return -ENOMEM;
2654be5e864SMauro Carvalho Chehab 
2664be5e864SMauro Carvalho Chehab 	cec->dev = &pdev->dev;
2674be5e864SMauro Carvalho Chehab 
268092c69b2SCai Huoqing 	mmio = devm_platform_ioremap_resource(pdev, 0);
2694be5e864SMauro Carvalho Chehab 	if (IS_ERR(mmio))
2704be5e864SMauro Carvalho Chehab 		return PTR_ERR(mmio);
2714be5e864SMauro Carvalho Chehab 
2724be5e864SMauro Carvalho Chehab 	cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio,
2734be5e864SMauro Carvalho Chehab 						&stm32_cec_regmap_cfg);
2744be5e864SMauro Carvalho Chehab 
2754be5e864SMauro Carvalho Chehab 	if (IS_ERR(cec->regmap))
2764be5e864SMauro Carvalho Chehab 		return PTR_ERR(cec->regmap);
2774be5e864SMauro Carvalho Chehab 
2784be5e864SMauro Carvalho Chehab 	cec->irq = platform_get_irq(pdev, 0);
2794be5e864SMauro Carvalho Chehab 	if (cec->irq < 0)
2804be5e864SMauro Carvalho Chehab 		return cec->irq;
2814be5e864SMauro Carvalho Chehab 
2824be5e864SMauro Carvalho Chehab 	ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
2834be5e864SMauro Carvalho Chehab 					stm32_cec_irq_handler,
2844be5e864SMauro Carvalho Chehab 					stm32_cec_irq_thread,
2854be5e864SMauro Carvalho Chehab 					0,
2864be5e864SMauro Carvalho Chehab 					pdev->name, cec);
2874be5e864SMauro Carvalho Chehab 	if (ret)
2884be5e864SMauro Carvalho Chehab 		return ret;
2894be5e864SMauro Carvalho Chehab 
2904be5e864SMauro Carvalho Chehab 	cec->clk_cec = devm_clk_get(&pdev->dev, "cec");
291*6cb7d1b3SYang Yingliang 	if (IS_ERR(cec->clk_cec))
292*6cb7d1b3SYang Yingliang 		return dev_err_probe(&pdev->dev, PTR_ERR(cec->clk_cec),
293*6cb7d1b3SYang Yingliang 				     "Cannot get cec clock\n");
2944be5e864SMauro Carvalho Chehab 
2954be5e864SMauro Carvalho Chehab 	ret = clk_prepare(cec->clk_cec);
2964be5e864SMauro Carvalho Chehab 	if (ret) {
2974be5e864SMauro Carvalho Chehab 		dev_err(&pdev->dev, "Unable to prepare cec clock\n");
2984be5e864SMauro Carvalho Chehab 		return ret;
2994be5e864SMauro Carvalho Chehab 	}
3004be5e864SMauro Carvalho Chehab 
3014be5e864SMauro Carvalho Chehab 	cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
3024be5e864SMauro Carvalho Chehab 	if (IS_ERR(cec->clk_hdmi_cec) &&
303055d2db2SEvgeny Novikov 	    PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) {
304055d2db2SEvgeny Novikov 		ret = -EPROBE_DEFER;
305055d2db2SEvgeny Novikov 		goto err_unprepare_cec_clk;
306055d2db2SEvgeny Novikov 	}
3074be5e864SMauro Carvalho Chehab 
3084be5e864SMauro Carvalho Chehab 	if (!IS_ERR(cec->clk_hdmi_cec)) {
3094be5e864SMauro Carvalho Chehab 		ret = clk_prepare(cec->clk_hdmi_cec);
3104be5e864SMauro Carvalho Chehab 		if (ret) {
3114be5e864SMauro Carvalho Chehab 			dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n");
312055d2db2SEvgeny Novikov 			goto err_unprepare_cec_clk;
3134be5e864SMauro Carvalho Chehab 		}
3144be5e864SMauro Carvalho Chehab 	}
3154be5e864SMauro Carvalho Chehab 
3164be5e864SMauro Carvalho Chehab 	/*
3174be5e864SMauro Carvalho Chehab 	 * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is
3184be5e864SMauro Carvalho Chehab 	 * available for example when a drm driver can provide edid
3194be5e864SMauro Carvalho Chehab 	 */
3204be5e864SMauro Carvalho Chehab 	cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec,
3214be5e864SMauro Carvalho Chehab 			CEC_NAME, caps,	CEC_MAX_LOG_ADDRS);
3224be5e864SMauro Carvalho Chehab 	ret = PTR_ERR_OR_ZERO(cec->adap);
3234be5e864SMauro Carvalho Chehab 	if (ret)
324055d2db2SEvgeny Novikov 		goto err_unprepare_hdmi_cec_clk;
3254be5e864SMauro Carvalho Chehab 
3264be5e864SMauro Carvalho Chehab 	ret = cec_register_adapter(cec->adap, &pdev->dev);
327055d2db2SEvgeny Novikov 	if (ret)
328055d2db2SEvgeny Novikov 		goto err_delete_adapter;
3294be5e864SMauro Carvalho Chehab 
3304be5e864SMauro Carvalho Chehab 	cec_hw_init(cec);
3314be5e864SMauro Carvalho Chehab 
3324be5e864SMauro Carvalho Chehab 	platform_set_drvdata(pdev, cec);
3334be5e864SMauro Carvalho Chehab 
3344be5e864SMauro Carvalho Chehab 	return 0;
335055d2db2SEvgeny Novikov 
336055d2db2SEvgeny Novikov err_delete_adapter:
337055d2db2SEvgeny Novikov 	cec_delete_adapter(cec->adap);
338055d2db2SEvgeny Novikov 
339055d2db2SEvgeny Novikov err_unprepare_hdmi_cec_clk:
340055d2db2SEvgeny Novikov 	clk_unprepare(cec->clk_hdmi_cec);
341055d2db2SEvgeny Novikov 
342055d2db2SEvgeny Novikov err_unprepare_cec_clk:
343055d2db2SEvgeny Novikov 	clk_unprepare(cec->clk_cec);
344055d2db2SEvgeny Novikov 	return ret;
3454be5e864SMauro Carvalho Chehab }
3464be5e864SMauro Carvalho Chehab 
3474be5e864SMauro Carvalho Chehab static int stm32_cec_remove(struct platform_device *pdev)
3484be5e864SMauro Carvalho Chehab {
3494be5e864SMauro Carvalho Chehab 	struct stm32_cec *cec = platform_get_drvdata(pdev);
3504be5e864SMauro Carvalho Chehab 
3514be5e864SMauro Carvalho Chehab 	clk_unprepare(cec->clk_cec);
3524be5e864SMauro Carvalho Chehab 	clk_unprepare(cec->clk_hdmi_cec);
3534be5e864SMauro Carvalho Chehab 
3544be5e864SMauro Carvalho Chehab 	cec_unregister_adapter(cec->adap);
3554be5e864SMauro Carvalho Chehab 
3564be5e864SMauro Carvalho Chehab 	return 0;
3574be5e864SMauro Carvalho Chehab }
3584be5e864SMauro Carvalho Chehab 
3594be5e864SMauro Carvalho Chehab static const struct of_device_id stm32_cec_of_match[] = {
3604be5e864SMauro Carvalho Chehab 	{ .compatible = "st,stm32-cec" },
3614be5e864SMauro Carvalho Chehab 	{ /* end node */ }
3624be5e864SMauro Carvalho Chehab };
3634be5e864SMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, stm32_cec_of_match);
3644be5e864SMauro Carvalho Chehab 
3654be5e864SMauro Carvalho Chehab static struct platform_driver stm32_cec_driver = {
3664be5e864SMauro Carvalho Chehab 	.probe  = stm32_cec_probe,
3674be5e864SMauro Carvalho Chehab 	.remove = stm32_cec_remove,
3684be5e864SMauro Carvalho Chehab 	.driver = {
3694be5e864SMauro Carvalho Chehab 		.name		= CEC_NAME,
3704be5e864SMauro Carvalho Chehab 		.of_match_table = stm32_cec_of_match,
3714be5e864SMauro Carvalho Chehab 	},
3724be5e864SMauro Carvalho Chehab };
3734be5e864SMauro Carvalho Chehab 
3744be5e864SMauro Carvalho Chehab module_platform_driver(stm32_cec_driver);
3754be5e864SMauro Carvalho Chehab 
3764be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
3774be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
3784be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control");
3794be5e864SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
380