1 From ed9e7a6b3346b186a7bd22d9b62072e55ff7b9c5 Mon Sep 17 00:00:00 2001
2 From: Sandor Yu <Sandor.yu@nxp.com>
3 Date: Mon, 2 Sep 2019 14:57:05 +0800
4 Subject: [PATCH] drm: bridge: cadence: Add CEC driver for cdns mhdp hdmi
6 Add cec driver for cdns mhdp hdmi.
8 Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
10 drivers/gpu/drm/bridge/cadence/Kconfig | 3 +
11 drivers/gpu/drm/bridge/cadence/Makefile | 1 +
12 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 0
13 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 9 +
14 drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 347 ++++++++++++++++++++++
15 drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 0
16 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 0
17 include/drm/bridge/cdns-mhdp-common.h | 24 +-
18 8 files changed, 382 insertions(+), 2 deletions(-)
19 mode change 100755 => 100644 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
20 mode change 100755 => 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
21 create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c
22 mode change 100755 => 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
23 mode change 100755 => 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
25 --- a/drivers/gpu/drm/bridge/cadence/Kconfig
26 +++ b/drivers/gpu/drm/bridge/cadence/Kconfig
27 @@ -14,3 +14,6 @@ config DRM_CDNS_DP
30 tristate "Cadence MHDP Audio driver"
32 +config DRM_CDNS_HDMI_CEC
33 + tristate "Cadence MHDP HDMI CEC driver"
34 --- a/drivers/gpu/drm/bridge/cadence/Makefile
35 +++ b/drivers/gpu/drm/bridge/cadence/Makefile
36 @@ -2,3 +2,4 @@ obj-$(CONFIG_DRM_CDNS_MHDP) += cdns-mhdp
37 obj-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o
38 obj-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o
39 obj-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o
40 +obj-$(CONFIG_DRM_CDNS_HDMI_CEC) += cdns-mhdp-cec.o
41 --- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
42 +++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
43 @@ -515,6 +515,11 @@ __cdns_hdmi_probe(struct platform_device
44 /* register audio driver */
45 cdns_mhdp_register_audio_driver(dev);
47 + /* register cec driver */
48 +#ifdef CONFIG_DRM_CDNS_HDMI_CEC
49 + cdns_mhdp_register_cec_driver(dev);
55 @@ -524,6 +529,10 @@ err_out:
57 static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp)
59 + /* unregister cec driver */
60 +#ifdef CONFIG_DRM_CDNS_HDMI_CEC
61 + cdns_mhdp_unregister_cec_driver(mhdp->dev);
63 cdns_mhdp_unregister_audio_driver(mhdp->dev);
67 +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c
70 + * Copyright 2019 NXP
72 + * This program is free software; you can redistribute it and/or
73 + * modify it under the terms of the GNU General Public License
74 + * as published by the Free Software Foundation; either version 2
75 + * of the License, or (at your option) any later version.
77 + * This program is distributed in the hope that it will be useful,
78 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
79 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80 + * GNU General Public License for more details.
82 +#include <linux/module.h>
83 +#include <linux/workqueue.h>
84 +#include <linux/kthread.h>
85 +#include <linux/freezer.h>
86 +#include <drm/bridge/cdns-mhdp-common.h>
88 +#define CEC_NAME "cdns-mhdp-cec"
90 +#define REG_ADDR_OFF 4
92 +#define MAX_LA_VAL 15
94 +/* regsiter define */
95 +#define TX_MSG_HEADER 0x33800
96 +#define TX_MSG_LENGTH 0x33840
97 +#define TX_MSG_CMD 0x33844
98 +#define RX_MSG_CMD 0x33850
99 +#define RX_CLEAR_BUF 0x33854
100 +#define LOGICAL_ADDRESS_LA0 0x33858
102 +#define CLK_DIV_MSB 0x3386c
103 +#define CLK_DIV_LSB 0x33870
104 +#define RX_MSG_DATA1 0x33900
105 +#define RX_MSG_LENGTH 0x33940
106 +#define RX_MSG_STATUS 0x33944
107 +#define NUM_OF_MSG_RX_BUF 0x33948
108 +#define TX_MSG_STATUS 0x3394c
109 +#define DB_L_TIMER 0x33980
112 + * CEC Transceiver operation.
118 + CEC_TX_ABORT_AND_TRANSMIT
122 + * CEC Transceiver status.
132 + * CEC Receiver operation.
138 + CEC_RX_ABORT_AND_CLR_FIFO
141 + * Maximum number of Messages in the RX Buffers.
143 +#define CEC_MAX_RX_MSGS 2
145 +static u32 mhdp_cec_read(struct cdns_mhdp_cec *cec, u32 offset)
147 + struct cdns_mhdp_device *mhdp =
148 + container_of(cec, struct cdns_mhdp_device, hdmi.cec);
149 + return cdns_mhdp_bus_read(mhdp, offset);
152 +static void mhdp_cec_write(struct cdns_mhdp_cec *cec, u32 offset, u32 val)
154 + struct cdns_mhdp_device *mhdp =
155 + container_of(cec, struct cdns_mhdp_device, hdmi.cec);
156 + cdns_mhdp_bus_write(val, mhdp, offset);
159 +static void mhdp_cec_clear_rx_buffer(struct cdns_mhdp_cec *cec)
161 + mhdp_cec_write(cec, RX_CLEAR_BUF, 1);
162 + mhdp_cec_write(cec, RX_CLEAR_BUF, 0);
165 +static void mhdp_cec_set_divider(struct cdns_mhdp_cec *cec)
167 + struct cdns_mhdp_device *mhdp =
168 + container_of(cec, struct cdns_mhdp_device, hdmi.cec);
171 + /* Set clock divider */
172 + clk_div = cdns_mhdp_get_fw_clk(mhdp) * 10;
174 + mhdp_cec_write(cec, CLK_DIV_MSB,
175 + (clk_div >> 8) & 0xFF);
176 + mhdp_cec_write(cec, CLK_DIV_LSB, clk_div & 0xFF);
179 +static u32 mhdp_cec_read_message(struct cdns_mhdp_cec *cec)
181 + struct cec_msg *msg = &cec->msg;
185 + mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_READ);
187 + len = mhdp_cec_read(cec, RX_MSG_LENGTH);
188 + msg->len = len + 1;
189 + dev_dbg(cec->dev, "RX MSG len =%d\n", len);
191 + /* Read RX MSG bytes */
192 + for (i = 0; i < msg->len; ++i) {
193 + msg->msg[i] = (u8) mhdp_cec_read(cec, RX_MSG_DATA1 + (i * REG_ADDR_OFF));
194 + dev_dbg(cec->dev, "RX MSG[%d]=0x%x\n", i, msg->msg[i]);
197 + mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_STOP);
202 +static u32 mhdp_cec_write_message(struct cdns_mhdp_cec *cec, struct cec_msg *msg)
206 + mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
208 + if (msg->len > CEC_MAX_MSG_SIZE) {
209 + dev_err(cec->dev, "Invalid MSG size!\n");
213 + for (i = 0; i < msg->len; ++i)
214 + printk("msg[%d]=0x%x\n",i, msg->msg[i]);
216 + /* Write Message to register */
217 + for (i = 0; i < msg->len; ++i) {
218 + mhdp_cec_write(cec, TX_MSG_HEADER + (i * REG_ADDR_OFF),
221 + /* Write Message Length (payload + opcode) */
222 + mhdp_cec_write(cec, TX_MSG_LENGTH, msg->len - 1);
224 + mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_TRANSMIT);
229 +//static void cec_abort_tx_transfer(struct cdns_mhdp_cec *cec)
231 +// cec_write(cec, TX_MSG_CMD, CEC_TX_ABORT);
232 +// cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
235 +static int mhdp_cec_set_logical_addr(struct cdns_mhdp_cec *cec, u32 la)
240 + if (la >= MAX_LA_VAL) {
241 + dev_err(cec->dev, "Error logical Addr\n");
245 + for (i = 0; i < MAX_LA_IDX; ++i) {
247 + mhdp_cec_read(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF));
252 + if ((la_reg & 0xF) == la) {
253 + dev_warn(cec->dev, "Warning. LA already in use.\n");
257 + la = (la & 0xF) | (1 << 4);
259 + mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), la);
263 + dev_warn(cec->dev, "All LA in use\n");
268 +static int mhdp_cec_poll_worker(void *_cec)
270 + struct cdns_mhdp_cec *cec = (struct cdns_mhdp_cec *)_cec;
271 + int num_rx_msgs, i;
277 + if (kthread_freezable_should_stop(NULL))
280 + /* Check TX State */
281 + sts = mhdp_cec_read(cec, TX_MSG_STATUS);
283 + case CEC_STS_SUCCESS:
284 + cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0,
286 + mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
288 + case CEC_STS_ERROR:
289 + mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
290 + cec_transmit_done(cec->adap,
291 + CEC_TX_STATUS_MAX_RETRIES |
292 + CEC_TX_STATUS_NACK, 0, 1, 0, 0);
299 + /* Check RX State */
300 + sts = mhdp_cec_read(cec, RX_MSG_STATUS);
301 + num_rx_msgs = mhdp_cec_read(cec, NUM_OF_MSG_RX_BUF);
303 + case CEC_STS_SUCCESS:
304 + if (num_rx_msgs == 0xf)
305 + num_rx_msgs = CEC_MAX_RX_MSGS;
307 + if (num_rx_msgs > CEC_MAX_RX_MSGS) {
308 + dev_err(cec->dev, "Error rx msg num %d\n",
310 + mhdp_cec_clear_rx_buffer(cec);
314 + /* Rx FIFO Depth 2 RX MSG */
315 + for (i = 0; i < num_rx_msgs; i++) {
316 + mhdp_cec_read_message(cec);
317 + cec->msg.rx_status = CEC_RX_STATUS_OK;
318 + cec_received_msg(cec->adap, &cec->msg);
325 + if (!kthread_should_stop())
326 + schedule_timeout_idle(20);
332 +static int mhdp_cec_adap_enable(struct cec_adapter *adap, bool enable)
334 + struct cdns_mhdp_cec *cec = adap->priv;
337 + mhdp_cec_write(cec, DB_L_TIMER, 0x10);
338 + mhdp_cec_set_divider(cec);
340 + mhdp_cec_set_divider(cec);
345 +static int mhdp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
347 + struct cdns_mhdp_cec *cec = adap->priv;
349 + return mhdp_cec_set_logical_addr(cec, addr);
352 +static int mhdp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
353 + u32 signal_free_time, struct cec_msg *msg)
355 + struct cdns_mhdp_cec *cec = adap->priv;
357 + mhdp_cec_write_message(cec, msg);
362 +static const struct cec_adap_ops cdns_mhdp_cec_adap_ops = {
363 + .adap_enable = mhdp_cec_adap_enable,
364 + .adap_log_addr = mhdp_cec_adap_log_addr,
365 + .adap_transmit = mhdp_cec_adap_transmit,
368 +int cdns_mhdp_register_cec_driver(struct device *dev)
370 + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
371 + struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec;
374 + cec->adap = cec_allocate_adapter(&cdns_mhdp_cec_adap_ops, cec,
376 + CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS |
377 + CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH
379 + ret = PTR_ERR_OR_ZERO(cec->adap);
382 + ret = cec_register_adapter(cec->adap, dev);
384 + cec_delete_adapter(cec->adap);
390 + cec->cec_worker = kthread_create(mhdp_cec_poll_worker, cec, "cdns-mhdp-cec");
391 + if (IS_ERR(cec->cec_worker))
392 + dev_err(cec->dev, "failed create hdp cec thread\n");
394 + wake_up_process(cec->cec_worker);
396 + dev_dbg(dev, "CEC successfuly probed\n");
400 +int cdns_mhdp_unregister_cec_driver(struct device *dev)
402 + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
403 + struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec;
405 + if (cec->cec_worker) {
406 + kthread_stop(cec->cec_worker);
407 + cec->cec_worker = NULL;
409 + cec_unregister_adapter(cec->adap);
413 +MODULE_AUTHOR("Sandor.Yu@NXP.com");
414 +MODULE_LICENSE("GPL");
415 +MODULE_DESCRIPTION("NXP CDNS MHDP CEC driver");
416 --- a/include/drm/bridge/cdns-mhdp-common.h
417 +++ b/include/drm/bridge/cdns-mhdp-common.h
419 #include <drm/drm_connector.h>
420 #include <drm/drm_dp_helper.h>
421 #include <drm/drm_dp_mst_helper.h>
423 +#include <media/cec.h>
424 #include <linux/bitops.h>
426 #define ADDR_IMEM 0x10000
427 @@ -605,6 +605,17 @@ struct cdns_mhdp_connector {
428 struct cdns_mhdp_bridge *bridge;
431 +#ifdef CONFIG_DRM_CDNS_HDMI_CEC
432 +struct cdns_mhdp_cec {
433 + struct cec_adapter *adap;
434 + struct device *dev;
437 + struct cec_msg msg;
438 + struct task_struct *cec_worker;
442 struct cdns_mhdp_device {
445 @@ -633,7 +644,7 @@ struct cdns_mhdp_device {
449 - struct cdn_dp_data {
451 struct drm_dp_link link;
452 struct drm_dp_aux aux;
453 struct cdns_mhdp_host host;
454 @@ -645,6 +656,9 @@ struct cdns_mhdp_device {
458 +#ifdef CONFIG_DRM_CDNS_HDMI_CEC
459 + struct cdns_mhdp_cec cec;
464 @@ -713,4 +727,10 @@ int cdns_hdmi_disable_gcp(struct cdns_mh
465 int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp);
467 bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp);
469 +#ifdef CONFIG_DRM_CDNS_HDMI_CEC
470 +int cdns_mhdp_register_cec_driver(struct device *dev);
471 +int cdns_mhdp_unregister_cec_driver(struct device *dev);
474 #endif /* CDNS_MHDP_COMMON_H_ */