18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STM32 CEC driver 48c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2017 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <media/cec.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define CEC_NAME "stm32-cec" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* CEC registers */ 228c2ecf20Sopenharmony_ci#define CEC_CR 0x0000 /* Control Register */ 238c2ecf20Sopenharmony_ci#define CEC_CFGR 0x0004 /* ConFiGuration Register */ 248c2ecf20Sopenharmony_ci#define CEC_TXDR 0x0008 /* Rx data Register */ 258c2ecf20Sopenharmony_ci#define CEC_RXDR 0x000C /* Rx data Register */ 268c2ecf20Sopenharmony_ci#define CEC_ISR 0x0010 /* Interrupt and status Register */ 278c2ecf20Sopenharmony_ci#define CEC_IER 0x0014 /* Interrupt enable Register */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TXEOM BIT(2) 308c2ecf20Sopenharmony_ci#define TXSOM BIT(1) 318c2ecf20Sopenharmony_ci#define CECEN BIT(0) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define LSTN BIT(31) 348c2ecf20Sopenharmony_ci#define OAR GENMASK(30, 16) 358c2ecf20Sopenharmony_ci#define SFTOP BIT(8) 368c2ecf20Sopenharmony_ci#define BRDNOGEN BIT(7) 378c2ecf20Sopenharmony_ci#define LBPEGEN BIT(6) 388c2ecf20Sopenharmony_ci#define BREGEN BIT(5) 398c2ecf20Sopenharmony_ci#define BRESTP BIT(4) 408c2ecf20Sopenharmony_ci#define RXTOL BIT(3) 418c2ecf20Sopenharmony_ci#define SFT GENMASK(2, 0) 428c2ecf20Sopenharmony_ci#define FULL_CFG (LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \ 438c2ecf20Sopenharmony_ci | RXTOL) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define TXACKE BIT(12) 468c2ecf20Sopenharmony_ci#define TXERR BIT(11) 478c2ecf20Sopenharmony_ci#define TXUDR BIT(10) 488c2ecf20Sopenharmony_ci#define TXEND BIT(9) 498c2ecf20Sopenharmony_ci#define TXBR BIT(8) 508c2ecf20Sopenharmony_ci#define ARBLST BIT(7) 518c2ecf20Sopenharmony_ci#define RXACKE BIT(6) 528c2ecf20Sopenharmony_ci#define RXOVR BIT(2) 538c2ecf20Sopenharmony_ci#define RXEND BIT(1) 548c2ecf20Sopenharmony_ci#define RXBR BIT(0) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST) 578c2ecf20Sopenharmony_ci#define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * 400 ms is the time it takes for one 16 byte message to be 618c2ecf20Sopenharmony_ci * transferred and 5 is the maximum number of retries. Add 628c2ecf20Sopenharmony_ci * another 100 ms as a margin. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct stm32_cec { 678c2ecf20Sopenharmony_ci struct cec_adapter *adap; 688c2ecf20Sopenharmony_ci struct device *dev; 698c2ecf20Sopenharmony_ci struct clk *clk_cec; 708c2ecf20Sopenharmony_ci struct clk *clk_hdmi_cec; 718c2ecf20Sopenharmony_ci struct reset_control *rstc; 728c2ecf20Sopenharmony_ci struct regmap *regmap; 738c2ecf20Sopenharmony_ci int irq; 748c2ecf20Sopenharmony_ci u32 irq_status; 758c2ecf20Sopenharmony_ci struct cec_msg rx_msg; 768c2ecf20Sopenharmony_ci struct cec_msg tx_msg; 778c2ecf20Sopenharmony_ci int tx_cnt; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void cec_hw_init(struct stm32_cec *cec) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT, 858c2ecf20Sopenharmony_ci ALL_TX_IT | ALL_RX_IT); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void stm32_tx_done(struct stm32_cec *cec, u32 status) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if (status & (TXERR | TXUDR)) { 938c2ecf20Sopenharmony_ci cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR, 948c2ecf20Sopenharmony_ci 0, 0, 0, 1); 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (status & ARBLST) { 998c2ecf20Sopenharmony_ci cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST, 1008c2ecf20Sopenharmony_ci 1, 0, 0, 0); 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (status & TXACKE) { 1058c2ecf20Sopenharmony_ci cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK, 1068c2ecf20Sopenharmony_ci 0, 1, 0, 0); 1078c2ecf20Sopenharmony_ci return; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (cec->irq_status & TXBR) { 1118c2ecf20Sopenharmony_ci /* send next byte */ 1128c2ecf20Sopenharmony_ci if (cec->tx_cnt < cec->tx_msg.len) 1138c2ecf20Sopenharmony_ci regmap_write(cec->regmap, CEC_TXDR, 1148c2ecf20Sopenharmony_ci cec->tx_msg.msg[cec->tx_cnt++]); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* TXEOM is set to command transmission of the last byte */ 1178c2ecf20Sopenharmony_ci if (cec->tx_cnt == cec->tx_msg.len) 1188c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (cec->irq_status & TXEND) 1228c2ecf20Sopenharmony_ci cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void stm32_rx_done(struct stm32_cec *cec, u32 status) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci if (cec->irq_status & (RXACKE | RXOVR)) { 1288c2ecf20Sopenharmony_ci cec->rx_msg.len = 0; 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (cec->irq_status & RXBR) { 1338c2ecf20Sopenharmony_ci u32 val; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci regmap_read(cec->regmap, CEC_RXDR, &val); 1368c2ecf20Sopenharmony_ci cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (cec->irq_status & RXEND) { 1408c2ecf20Sopenharmony_ci cec_received_msg(cec->adap, &cec->rx_msg); 1418c2ecf20Sopenharmony_ci cec->rx_msg.len = 0; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic irqreturn_t stm32_cec_irq_thread(int irq, void *arg) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct stm32_cec *cec = arg; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (cec->irq_status & ALL_TX_IT) 1508c2ecf20Sopenharmony_ci stm32_tx_done(cec, cec->irq_status); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (cec->irq_status & ALL_RX_IT) 1538c2ecf20Sopenharmony_ci stm32_rx_done(cec, cec->irq_status); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci cec->irq_status = 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic irqreturn_t stm32_cec_irq_handler(int irq, void *arg) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct stm32_cec *cec = arg; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci regmap_read(cec->regmap, CEC_ISR, &cec->irq_status); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_ISR, 1678c2ecf20Sopenharmony_ci ALL_TX_IT | ALL_RX_IT, 1688c2ecf20Sopenharmony_ci ALL_TX_IT | ALL_RX_IT); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct stm32_cec *cec = adap->priv; 1768c2ecf20Sopenharmony_ci int ret = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (enable) { 1798c2ecf20Sopenharmony_ci ret = clk_enable(cec->clk_cec); 1808c2ecf20Sopenharmony_ci if (ret) 1818c2ecf20Sopenharmony_ci dev_err(cec->dev, "fail to enable cec clock\n"); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci clk_enable(cec->clk_hdmi_cec); 1848c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); 1858c2ecf20Sopenharmony_ci } else { 1868c2ecf20Sopenharmony_ci clk_disable(cec->clk_cec); 1878c2ecf20Sopenharmony_ci clk_disable(cec->clk_hdmi_cec); 1888c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct stm32_cec *cec = adap->priv; 1978c2ecf20Sopenharmony_ci u32 oar = (1 << logical_addr) << 16; 1988c2ecf20Sopenharmony_ci u32 val; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Poll every 100µs the register CEC_CR to wait end of transmission */ 2018c2ecf20Sopenharmony_ci regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM), 2028c2ecf20Sopenharmony_ci 100, CEC_XFER_TIMEOUT_MS * 1000); 2038c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (logical_addr == CEC_LOG_ADDR_INVALID) 2068c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0); 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 2168c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct stm32_cec *cec = adap->priv; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Copy message */ 2218c2ecf20Sopenharmony_ci cec->tx_msg = *msg; 2228c2ecf20Sopenharmony_ci cec->tx_cnt = 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * If the CEC message consists of only one byte, 2268c2ecf20Sopenharmony_ci * TXEOM must be set before of TXSOM. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci if (cec->tx_msg.len == 1) 2298c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* TXSOM is set to command transmission of the first byte */ 2328c2ecf20Sopenharmony_ci regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Write the header (first byte of message) */ 2358c2ecf20Sopenharmony_ci regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]); 2368c2ecf20Sopenharmony_ci cec->tx_cnt++; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct cec_adap_ops stm32_cec_adap_ops = { 2428c2ecf20Sopenharmony_ci .adap_enable = stm32_cec_adap_enable, 2438c2ecf20Sopenharmony_ci .adap_log_addr = stm32_cec_adap_log_addr, 2448c2ecf20Sopenharmony_ci .adap_transmit = stm32_cec_adap_transmit, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct regmap_config stm32_cec_regmap_cfg = { 2488c2ecf20Sopenharmony_ci .reg_bits = 32, 2498c2ecf20Sopenharmony_ci .val_bits = 32, 2508c2ecf20Sopenharmony_ci .reg_stride = sizeof(u32), 2518c2ecf20Sopenharmony_ci .max_register = 0x14, 2528c2ecf20Sopenharmony_ci .fast_io = true, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int stm32_cec_probe(struct platform_device *pdev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_MODE_MONITOR_ALL; 2588c2ecf20Sopenharmony_ci struct resource *res; 2598c2ecf20Sopenharmony_ci struct stm32_cec *cec; 2608c2ecf20Sopenharmony_ci void __iomem *mmio; 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); 2648c2ecf20Sopenharmony_ci if (!cec) 2658c2ecf20Sopenharmony_ci return -ENOMEM; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci cec->dev = &pdev->dev; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2708c2ecf20Sopenharmony_ci mmio = devm_ioremap_resource(&pdev->dev, res); 2718c2ecf20Sopenharmony_ci if (IS_ERR(mmio)) 2728c2ecf20Sopenharmony_ci return PTR_ERR(mmio); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio, 2758c2ecf20Sopenharmony_ci &stm32_cec_regmap_cfg); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (IS_ERR(cec->regmap)) 2788c2ecf20Sopenharmony_ci return PTR_ERR(cec->regmap); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci cec->irq = platform_get_irq(pdev, 0); 2818c2ecf20Sopenharmony_ci if (cec->irq < 0) 2828c2ecf20Sopenharmony_ci return cec->irq; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, cec->irq, 2858c2ecf20Sopenharmony_ci stm32_cec_irq_handler, 2868c2ecf20Sopenharmony_ci stm32_cec_irq_thread, 2878c2ecf20Sopenharmony_ci 0, 2888c2ecf20Sopenharmony_ci pdev->name, cec); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci cec->clk_cec = devm_clk_get(&pdev->dev, "cec"); 2938c2ecf20Sopenharmony_ci if (IS_ERR(cec->clk_cec)) { 2948c2ecf20Sopenharmony_ci if (PTR_ERR(cec->clk_cec) != -EPROBE_DEFER) 2958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot get cec clock\n"); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return PTR_ERR(cec->clk_cec); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = clk_prepare(cec->clk_cec); 3018c2ecf20Sopenharmony_ci if (ret) { 3028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to prepare cec clock\n"); 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec"); 3078c2ecf20Sopenharmony_ci if (IS_ERR(cec->clk_hdmi_cec) && 3088c2ecf20Sopenharmony_ci PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) { 3098c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3108c2ecf20Sopenharmony_ci goto err_unprepare_cec_clk; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!IS_ERR(cec->clk_hdmi_cec)) { 3148c2ecf20Sopenharmony_ci ret = clk_prepare(cec->clk_hdmi_cec); 3158c2ecf20Sopenharmony_ci if (ret) { 3168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n"); 3178c2ecf20Sopenharmony_ci goto err_unprepare_cec_clk; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is 3238c2ecf20Sopenharmony_ci * available for example when a drm driver can provide edid 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec, 3268c2ecf20Sopenharmony_ci CEC_NAME, caps, CEC_MAX_LOG_ADDRS); 3278c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(cec->adap); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci goto err_unprepare_hdmi_cec_clk; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ret = cec_register_adapter(cec->adap, &pdev->dev); 3328c2ecf20Sopenharmony_ci if (ret) 3338c2ecf20Sopenharmony_ci goto err_delete_adapter; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci cec_hw_init(cec); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, cec); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cierr_delete_adapter: 3428c2ecf20Sopenharmony_ci cec_delete_adapter(cec->adap); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cierr_unprepare_hdmi_cec_clk: 3458c2ecf20Sopenharmony_ci clk_unprepare(cec->clk_hdmi_cec); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cierr_unprepare_cec_clk: 3488c2ecf20Sopenharmony_ci clk_unprepare(cec->clk_cec); 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int stm32_cec_remove(struct platform_device *pdev) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct stm32_cec *cec = platform_get_drvdata(pdev); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci clk_unprepare(cec->clk_cec); 3578c2ecf20Sopenharmony_ci clk_unprepare(cec->clk_hdmi_cec); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci cec_unregister_adapter(cec->adap); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_cec_of_match[] = { 3658c2ecf20Sopenharmony_ci { .compatible = "st,stm32-cec" }, 3668c2ecf20Sopenharmony_ci { /* end node */ } 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_cec_of_match); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic struct platform_driver stm32_cec_driver = { 3718c2ecf20Sopenharmony_ci .probe = stm32_cec_probe, 3728c2ecf20Sopenharmony_ci .remove = stm32_cec_remove, 3738c2ecf20Sopenharmony_ci .driver = { 3748c2ecf20Sopenharmony_ci .name = CEC_NAME, 3758c2ecf20Sopenharmony_ci .of_match_table = stm32_cec_of_match, 3768c2ecf20Sopenharmony_ci }, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cimodule_platform_driver(stm32_cec_driver); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 3828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 3838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control"); 3848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 385