brintos

brintos / linux-shallow public Read only

0
0
Text · 12.3 KiB · 7c1022c Raw
482 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Tegra CEC implementation4 *5 * The original 3.10 CEC driver using a custom API:6 *7 * Copyright (c) 2012-2015, NVIDIA CORPORATION.  All rights reserved.8 *9 * Conversion to the CEC framework and to the mainline kernel:10 *11 * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.12 */13 14#include <linux/module.h>15#include <linux/kernel.h>16#include <linux/err.h>17#include <linux/errno.h>18#include <linux/interrupt.h>19#include <linux/slab.h>20#include <linux/io.h>21#include <linux/clk.h>22#include <linux/delay.h>23#include <linux/pm.h>24#include <linux/of.h>25#include <linux/of_platform.h>26#include <linux/platform_device.h>27#include <linux/clk/tegra.h>28 29#include <media/cec-notifier.h>30 31#include "tegra_cec.h"32 33#define TEGRA_CEC_NAME "tegra-cec"34 35struct tegra_cec {36	struct cec_adapter	*adap;37	struct device		*dev;38	struct clk		*clk;39	void __iomem		*cec_base;40	struct cec_notifier	*notifier;41	int			tegra_cec_irq;42	bool			rx_done;43	bool			tx_done;44	int			tx_status;45	u8			rx_buf[CEC_MAX_MSG_SIZE];46	u8			rx_buf_cnt;47	u32			tx_buf[CEC_MAX_MSG_SIZE];48	u8			tx_buf_cur;49	u8			tx_buf_cnt;50};51 52static inline u32 cec_read(struct tegra_cec *cec, u32 reg)53{54	return readl(cec->cec_base + reg);55}56 57static inline void cec_write(struct tegra_cec *cec, u32 reg, u32 val)58{59	writel(val, cec->cec_base + reg);60}61 62static void tegra_cec_error_recovery(struct tegra_cec *cec)63{64	u32 hw_ctrl;65 66	hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL);67	cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);68	cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);69	cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl);70}71 72static irqreturn_t tegra_cec_irq_thread_handler(int irq, void *data)73{74	struct device *dev = data;75	struct tegra_cec *cec = dev_get_drvdata(dev);76 77	if (cec->tx_done) {78		cec_transmit_attempt_done(cec->adap, cec->tx_status);79		cec->tx_done = false;80	}81	if (cec->rx_done) {82		struct cec_msg msg = {};83 84		msg.len = cec->rx_buf_cnt;85		memcpy(msg.msg, cec->rx_buf, msg.len);86		cec_received_msg(cec->adap, &msg);87		cec->rx_done = false;88		cec->rx_buf_cnt = 0;89	}90	return IRQ_HANDLED;91}92 93static irqreturn_t tegra_cec_irq_handler(int irq, void *data)94{95	struct device *dev = data;96	struct tegra_cec *cec = dev_get_drvdata(dev);97	u32 status, mask;98 99	status = cec_read(cec, TEGRA_CEC_INT_STAT);100	mask = cec_read(cec, TEGRA_CEC_INT_MASK);101 102	status &= mask;103 104	if (!status)105		return IRQ_HANDLED;106 107	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {108		dev_err(dev, "TX underrun, interrupt timing issue!\n");109 110		tegra_cec_error_recovery(cec);111		cec_write(cec, TEGRA_CEC_INT_MASK,112			  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);113 114		cec->tx_done = true;115		cec->tx_status = CEC_TX_STATUS_ERROR;116		return IRQ_WAKE_THREAD;117	}118 119	if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||120		   (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {121		tegra_cec_error_recovery(cec);122		cec_write(cec, TEGRA_CEC_INT_MASK,123			  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);124 125		cec->tx_done = true;126		if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)127			cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;128		else129			cec->tx_status = CEC_TX_STATUS_ARB_LOST;130		return IRQ_WAKE_THREAD;131	}132 133	if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {134		cec_write(cec, TEGRA_CEC_INT_STAT,135			  TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED);136 137		if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {138			tegra_cec_error_recovery(cec);139 140			cec->tx_done = true;141			cec->tx_status = CEC_TX_STATUS_NACK;142		} else {143			cec->tx_done = true;144			cec->tx_status = CEC_TX_STATUS_OK;145		}146		return IRQ_WAKE_THREAD;147	}148 149	if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)150		dev_warn(dev, "TX NAKed on the fly!\n");151 152	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {153		if (cec->tx_buf_cur == cec->tx_buf_cnt) {154			cec_write(cec, TEGRA_CEC_INT_MASK,155				  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);156		} else {157			cec_write(cec, TEGRA_CEC_TX_REGISTER,158				  cec->tx_buf[cec->tx_buf_cur++]);159			cec_write(cec, TEGRA_CEC_INT_STAT,160				  TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY);161		}162	}163 164	if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) {165		cec_write(cec, TEGRA_CEC_INT_STAT,166			  TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED);167		cec->rx_done = false;168		cec->rx_buf_cnt = 0;169	}170	if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {171		u32 v;172 173		cec_write(cec, TEGRA_CEC_INT_STAT,174			  TEGRA_CEC_INT_STAT_RX_REGISTER_FULL);175		v = cec_read(cec, TEGRA_CEC_RX_REGISTER);176		if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE)177			cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff;178		if (v & TEGRA_CEC_RX_REGISTER_EOM) {179			cec->rx_done = true;180			return IRQ_WAKE_THREAD;181		}182	}183 184	return IRQ_HANDLED;185}186 187static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)188{189	struct tegra_cec *cec = adap->priv;190 191	cec->rx_buf_cnt = 0;192	cec->tx_buf_cnt = 0;193	cec->tx_buf_cur = 0;194 195	cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);196	cec_write(cec, TEGRA_CEC_INT_MASK, 0);197	cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);198	cec_write(cec, TEGRA_CEC_SW_CONTROL, 0);199 200	if (!enable)201		return 0;202 203	cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20);204 205	cec_write(cec, TEGRA_CEC_RX_TIMING_0,206		  (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) |207		  (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) |208		  (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) |209		  (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT));210 211	cec_write(cec, TEGRA_CEC_RX_TIMING_1,212		  (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) |213		  (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) |214		  (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) |215		  (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT));216 217	cec_write(cec, TEGRA_CEC_RX_TIMING_2,218		  (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT));219 220	cec_write(cec, TEGRA_CEC_TX_TIMING_0,221		  (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) |222		  (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) |223		  (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) |224		  (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT));225 226	cec_write(cec, TEGRA_CEC_TX_TIMING_1,227		  (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) |228		  (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) |229		  (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) |230		  (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT));231 232	cec_write(cec, TEGRA_CEC_TX_TIMING_2,233		  (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) |234		  (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) |235		  (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT));236 237	cec_write(cec, TEGRA_CEC_INT_MASK,238		  TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |239		  TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |240		  TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |241		  TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |242		  TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |243		  TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |244		  TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED);245 246	cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);247	return 0;248}249 250static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)251{252	struct tegra_cec *cec = adap->priv;253	u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL);254 255	if (logical_addr == CEC_LOG_ADDR_INVALID)256		state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK;257	else258		state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr));259 260	cec_write(cec, TEGRA_CEC_HW_CONTROL, state);261	return 0;262}263 264static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap,265					     bool enable)266{267	struct tegra_cec *cec = adap->priv;268	u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL);269 270	if (enable)271		reg |= TEGRA_CEC_HWCTRL_RX_SNOOP;272	else273		reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP;274	cec_write(cec, TEGRA_CEC_HW_CONTROL, reg);275	return 0;276}277 278static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,279				   u32 signal_free_time_ms, struct cec_msg *msg)280{281	bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY;282	struct tegra_cec *cec = adap->priv;283	unsigned int i;284	u32 mode = 0;285	u32 mask;286 287	if (cec_msg_is_broadcast(msg))288		mode = TEGRA_CEC_TX_REG_BCAST;289 290	cec->tx_buf_cur = 0;291	cec->tx_buf_cnt = msg->len;292 293	for (i = 0; i < msg->len; i++) {294		cec->tx_buf[i] = mode | msg->msg[i];295		if (i == 0)296			cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT;297		if (i == msg->len - 1)298			cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM;299		if (i == 0 && retry_xfer)300			cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY;301	}302 303	mask = cec_read(cec, TEGRA_CEC_INT_MASK);304	cec_write(cec, TEGRA_CEC_INT_MASK,305		  mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);306 307	return 0;308}309 310static const struct cec_adap_ops tegra_cec_ops = {311	.adap_enable = tegra_cec_adap_enable,312	.adap_log_addr = tegra_cec_adap_log_addr,313	.adap_transmit = tegra_cec_adap_transmit,314	.adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable,315};316 317static int tegra_cec_probe(struct platform_device *pdev)318{319	struct device *hdmi_dev;320	struct tegra_cec *cec;321	struct resource *res;322	int ret = 0;323 324	hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);325 326	if (IS_ERR(hdmi_dev))327		return PTR_ERR(hdmi_dev);328 329	cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL);330 331	if (!cec)332		return -ENOMEM;333 334	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);335 336	if (!res) {337		dev_err(&pdev->dev,338			"Unable to allocate resources for device\n");339		return -EBUSY;340	}341 342	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),343		pdev->name)) {344		dev_err(&pdev->dev,345			"Unable to request mem region for device\n");346		return -EBUSY;347	}348 349	cec->tegra_cec_irq = platform_get_irq(pdev, 0);350 351	if (cec->tegra_cec_irq < 0)352		return cec->tegra_cec_irq;353 354	cec->cec_base = devm_ioremap(&pdev->dev, res->start,355					     resource_size(res));356 357	if (!cec->cec_base) {358		dev_err(&pdev->dev, "Unable to grab IOs for device\n");359		return -EBUSY;360	}361 362	cec->clk = devm_clk_get(&pdev->dev, "cec");363 364	if (IS_ERR_OR_NULL(cec->clk)) {365		dev_err(&pdev->dev, "Can't get clock for CEC\n");366		return -ENOENT;367	}368 369	ret = clk_prepare_enable(cec->clk);370	if (ret) {371		dev_err(&pdev->dev, "Unable to prepare clock for CEC\n");372		return ret;373	}374 375	/* set context info. */376	cec->dev = &pdev->dev;377 378	platform_set_drvdata(pdev, cec);379 380	ret = devm_request_threaded_irq(&pdev->dev, cec->tegra_cec_irq,381		tegra_cec_irq_handler, tegra_cec_irq_thread_handler,382		0, "cec_irq", &pdev->dev);383 384	if (ret) {385		dev_err(&pdev->dev,386			"Unable to request interrupt for device\n");387		goto err_clk;388	}389 390	cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,391			CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL |392			CEC_CAP_CONNECTOR_INFO,393			CEC_MAX_LOG_ADDRS);394	if (IS_ERR(cec->adap)) {395		ret = -ENOMEM;396		dev_err(&pdev->dev, "Couldn't create cec adapter\n");397		goto err_clk;398	}399 400	cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,401						       cec->adap);402	if (!cec->notifier) {403		ret = -ENOMEM;404		goto err_adapter;405	}406 407	ret = cec_register_adapter(cec->adap, &pdev->dev);408	if (ret) {409		dev_err(&pdev->dev, "Couldn't register device\n");410		goto err_notifier;411	}412 413	return 0;414 415err_notifier:416	cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);417err_adapter:418	cec_delete_adapter(cec->adap);419err_clk:420	clk_disable_unprepare(cec->clk);421	return ret;422}423 424static void tegra_cec_remove(struct platform_device *pdev)425{426	struct tegra_cec *cec = platform_get_drvdata(pdev);427 428	clk_disable_unprepare(cec->clk);429 430	cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);431	cec_unregister_adapter(cec->adap);432}433 434#ifdef CONFIG_PM435static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state)436{437	struct tegra_cec *cec = platform_get_drvdata(pdev);438 439	clk_disable_unprepare(cec->clk);440 441	dev_notice(&pdev->dev, "suspended\n");442	return 0;443}444 445static int tegra_cec_resume(struct platform_device *pdev)446{447	struct tegra_cec *cec = platform_get_drvdata(pdev);448 449	dev_notice(&pdev->dev, "Resuming\n");450 451	return clk_prepare_enable(cec->clk);452}453#endif454 455static const struct of_device_id tegra_cec_of_match[] = {456	{ .compatible = "nvidia,tegra114-cec", },457	{ .compatible = "nvidia,tegra124-cec", },458	{ .compatible = "nvidia,tegra210-cec", },459	{},460};461 462static struct platform_driver tegra_cec_driver = {463	.driver = {464		.name = TEGRA_CEC_NAME,465		.of_match_table = tegra_cec_of_match,466	},467	.probe = tegra_cec_probe,468	.remove_new = tegra_cec_remove,469 470#ifdef CONFIG_PM471	.suspend = tegra_cec_suspend,472	.resume = tegra_cec_resume,473#endif474};475 476module_platform_driver(tegra_cec_driver);477 478MODULE_DESCRIPTION("Tegra HDMI CEC driver");479MODULE_AUTHOR("NVIDIA CORPORATION");480MODULE_AUTHOR("Cisco Systems, Inc. and/or its affiliates");481MODULE_LICENSE("GPL v2");482