blob: f15272e8a5ea090ea6ee6b5bf21823559ce77c3f [file] [log] [blame]
Sachin Bhayareeeb88892018-01-02 16:36:01 +05301/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/io.h>
14#include <linux/list.h>
15#include <linux/types.h>
16#include <linux/stat.h>
17#include <linux/slab.h>
18#include <linux/device.h>
19#include <linux/input.h>
20
21#include "mdss_hdmi_cec.h"
22#include "mdss_panel.h"
23
24#define CEC_STATUS_WR_ERROR BIT(0)
25#define CEC_STATUS_WR_DONE BIT(1)
26#define CEC_INTR (BIT(1) | BIT(3) | BIT(7))
27
28#define CEC_SUPPORTED_HW_VERSION 0x30000001
29
30/* Reference: HDMI 1.4a Specification section 7.1 */
31
32#define CEC_OP_SET_STREAM_PATH 0x86
33#define CEC_OP_KEY_PRESS 0x44
34#define CEC_OP_STANDBY 0x36
35
36struct hdmi_cec_ctrl {
37 bool cec_enabled;
38 bool cec_wakeup_en;
39 bool cec_device_suspend;
40
41 u32 cec_msg_wr_status;
42 spinlock_t lock;
43 struct work_struct cec_read_work;
44 struct completion cec_msg_wr_done;
45 struct hdmi_cec_init_data init_data;
46 struct input_dev *input;
47};
48
49static int hdmi_cec_msg_send(void *data, struct cec_msg *msg)
50{
51 int i, line_check_retry = 10, rc = 0;
52 u32 frame_retransmit = RETRANSMIT_MAX_NUM;
53 bool frame_type;
54 unsigned long flags;
Sachin Bhayare5076e252018-01-18 14:56:45 +053055 struct mdss_io_data *io = NULL;
Sachin Bhayareeeb88892018-01-02 16:36:01 +053056 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)data;
57
58 if (!cec_ctrl || !cec_ctrl->init_data.io || !msg) {
59 DEV_ERR("%s: Invalid input\n", __func__);
60 return -EINVAL;
61 }
62
63 io = cec_ctrl->init_data.io;
64
65 reinit_completion(&cec_ctrl->cec_msg_wr_done);
66 cec_ctrl->cec_msg_wr_status = 0;
67 frame_type = (msg->recvr_id == 15 ? BIT(0) : 0);
68 if (msg->retransmit > 0 && msg->retransmit < RETRANSMIT_MAX_NUM)
69 frame_retransmit = msg->retransmit;
70
71 /* toggle cec in order to flush out bad hw state, if any */
72 DSS_REG_W(io, HDMI_CEC_CTRL, 0);
73 DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
74
75 frame_retransmit = (frame_retransmit & 0xF) << 4;
76 DSS_REG_W(io, HDMI_CEC_RETRANSMIT, BIT(0) | frame_retransmit);
77
78 /* header block */
79 DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
80 (((msg->sender_id << 4) | msg->recvr_id) << 8) | frame_type);
81
82 /* data block 0 : opcode */
83 DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
84 ((msg->frame_size < 2 ? 0 : msg->opcode) << 8) | frame_type);
85
86 /* data block 1-14 : operand 0-13 */
87 for (i = 0; i < msg->frame_size - 2; i++)
88 DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
89 (msg->operand[i] << 8) | frame_type);
90
91 while ((DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0)) &&
92 line_check_retry) {
93 line_check_retry--;
94 DEV_DBG("%s: CEC line is busy(%d)\n", __func__,
95 line_check_retry);
96 schedule();
97 }
98
99 if (!line_check_retry && (DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0))) {
100 DEV_ERR("%s: CEC line is busy. Retry\n", __func__);
101 return -EAGAIN;
102 }
103
104 /* start transmission */
105 DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0) | BIT(1) |
106 ((msg->frame_size & 0x1F) << 4) | BIT(9));
107
108 if (!wait_for_completion_timeout(
109 &cec_ctrl->cec_msg_wr_done, HZ)) {
110 DEV_ERR("%s: timedout", __func__);
111 return -ETIMEDOUT;
112 }
113
114 spin_lock_irqsave(&cec_ctrl->lock, flags);
115 if (cec_ctrl->cec_msg_wr_status == CEC_STATUS_WR_ERROR) {
116 rc = -ENXIO;
117 DEV_ERR("%s: msg write failed.\n", __func__);
118 } else {
119 DEV_DBG("%s: CEC write frame done (frame len=%d)", __func__,
120 msg->frame_size);
121 }
122 spin_unlock_irqrestore(&cec_ctrl->lock, flags);
123
124 return rc;
125} /* hdmi_cec_msg_send */
126
127static void hdmi_cec_init_input_event(struct hdmi_cec_ctrl *cec_ctrl)
128{
129 int rc = 0;
130
131 if (!cec_ctrl) {
132 DEV_ERR("%s: Invalid input\n", __func__);
133 return;
134 }
135
136 /* Initialize CEC input events */
137 if (!cec_ctrl->input)
138 cec_ctrl->input = input_allocate_device();
139 if (!cec_ctrl->input) {
140 DEV_ERR("%s: hdmi input device allocation failed\n", __func__);
141 return;
142 }
143
144 cec_ctrl->input->name = "HDMI CEC User or Deck Control";
145 cec_ctrl->input->phys = "hdmi/input0";
146 cec_ctrl->input->id.bustype = BUS_VIRTUAL;
147
148 input_set_capability(cec_ctrl->input, EV_KEY, KEY_POWER);
149
150 rc = input_register_device(cec_ctrl->input);
151 if (rc) {
152 DEV_ERR("%s: cec input device registeration failed\n",
153 __func__);
154 input_free_device(cec_ctrl->input);
155 cec_ctrl->input = NULL;
156 return;
157 }
158}
159
160static void hdmi_cec_deinit_input_event(struct hdmi_cec_ctrl *cec_ctrl)
161{
162 if (cec_ctrl->input)
163 input_unregister_device(cec_ctrl->input);
164 cec_ctrl->input = NULL;
165}
166
167static void hdmi_cec_msg_recv(struct work_struct *work)
168{
169 int i;
170 u32 data;
171 struct hdmi_cec_ctrl *cec_ctrl = NULL;
Sachin Bhayare5076e252018-01-18 14:56:45 +0530172 struct mdss_io_data *io = NULL;
Sachin Bhayareeeb88892018-01-02 16:36:01 +0530173 struct cec_msg msg;
174 struct cec_cbs *cbs;
175
176 cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work);
177 if (!cec_ctrl || !cec_ctrl->init_data.io) {
178 DEV_ERR("%s: invalid input\n", __func__);
179 return;
180 }
181
182 if (!cec_ctrl->cec_enabled) {
183 DEV_ERR("%s: cec not enabled\n", __func__);
184 return;
185 }
186
187 io = cec_ctrl->init_data.io;
188 cbs = cec_ctrl->init_data.cbs;
189
190 data = DSS_REG_R(io, HDMI_CEC_RD_DATA);
191
192 msg.recvr_id = (data & 0x000F);
193 msg.sender_id = (data & 0x00F0) >> 4;
194 msg.frame_size = (data & 0x1F00) >> 8;
195 DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__,
196 msg.sender_id, msg.recvr_id,
197 msg.frame_size);
198
199 if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) {
200 DEV_ERR("%s: invalid message (frame length = %d)\n",
201 __func__, msg.frame_size);
202 return;
203 } else if (msg.frame_size == 1) {
204 DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n",
205 __func__, msg.recvr_id, msg.sender_id);
206 return;
207 }
208
209 /* data block 0 : opcode */
210 data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
211 msg.opcode = data & 0xFF;
212
213 /* data block 1-14 : operand 0-13 */
214 for (i = 0; i < msg.frame_size - 2; i++) {
215 data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
216 msg.operand[i] = data & 0xFF;
217 }
218
219 for (; i < MAX_OPERAND_SIZE; i++)
220 msg.operand[i] = 0;
221
222 DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__,
223 msg.opcode, cec_ctrl->cec_wakeup_en,
224 cec_ctrl->cec_device_suspend);
225
226 if ((msg.opcode == CEC_OP_SET_STREAM_PATH ||
227 msg.opcode == CEC_OP_KEY_PRESS) &&
228 cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
229 cec_ctrl->cec_device_suspend) {
230 DEV_DBG("%s: Sending power on at wakeup\n", __func__);
231 input_report_key(cec_ctrl->input, KEY_POWER, 1);
232 input_sync(cec_ctrl->input);
233 input_report_key(cec_ctrl->input, KEY_POWER, 0);
234 input_sync(cec_ctrl->input);
235 }
236
237 if ((msg.opcode == CEC_OP_STANDBY) &&
238 cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
239 !cec_ctrl->cec_device_suspend) {
240 DEV_DBG("%s: Sending power off on standby\n", __func__);
241 input_report_key(cec_ctrl->input, KEY_POWER, 1);
242 input_sync(cec_ctrl->input);
243 input_report_key(cec_ctrl->input, KEY_POWER, 0);
244 input_sync(cec_ctrl->input);
245 }
246
247 if (cbs && cbs->msg_recv_notify)
248 cbs->msg_recv_notify(cbs->data, &msg);
249}
250
251/**
252 * hdmi_cec_isr() - interrupt handler for cec hw module
253 * @cec_ctrl: pointer to cec hw module's data
254 *
255 * Return: irq error code
256 *
257 * The API can be called by HDMI Tx driver on receiving hw interrupts
258 * to let the CEC related interrupts handled by this module.
259 */
260int hdmi_cec_isr(void *input)
261{
262 int rc = 0;
263 u32 cec_intr, cec_status;
264 unsigned long flags;
Sachin Bhayare5076e252018-01-18 14:56:45 +0530265 struct mdss_io_data *io = NULL;
Sachin Bhayareeeb88892018-01-02 16:36:01 +0530266 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
267
268 if (!cec_ctrl || !cec_ctrl->init_data.io) {
269 DEV_ERR("%s: Invalid input\n", __func__);
270 return -EPERM;
271 }
272
273 if (!cec_ctrl->cec_enabled) {
274 DEV_DBG("%s: CEC feature not enabled\n", __func__);
275 return 0;
276 }
277
278 io = cec_ctrl->init_data.io;
279
280 cec_intr = DSS_REG_R_ND(io, HDMI_CEC_INT);
281
282 cec_status = DSS_REG_R_ND(io, HDMI_CEC_STATUS);
283
284 if ((cec_intr & BIT(0)) && (cec_intr & BIT(1))) {
285 DEV_DBG("%s: CEC_IRQ_FRAME_WR_DONE\n", __func__);
286 DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(0));
287
288 spin_lock_irqsave(&cec_ctrl->lock, flags);
289 cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_DONE;
290 spin_unlock_irqrestore(&cec_ctrl->lock, flags);
291
292 if (!completion_done(&cec_ctrl->cec_msg_wr_done))
293 complete_all(&cec_ctrl->cec_msg_wr_done);
294 }
295
296 if ((cec_intr & BIT(2)) && (cec_intr & BIT(3))) {
297 DEV_DBG("%s: CEC_IRQ_FRAME_ERROR\n", __func__);
298 DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(2));
299
300 spin_lock_irqsave(&cec_ctrl->lock, flags);
301 cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_ERROR;
302 spin_unlock_irqrestore(&cec_ctrl->lock, flags);
303
304 if (!completion_done(&cec_ctrl->cec_msg_wr_done))
305 complete_all(&cec_ctrl->cec_msg_wr_done);
306 }
307
308 if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) {
309 DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__);
310
311 DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(6));
312 queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work);
313 }
314
315 return rc;
316}
317
318void hdmi_cec_device_suspend(void *input, bool suspend)
319{
320 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
321
322 if (!cec_ctrl) {
323 DEV_WARN("%s: HDMI CEC HW module not initialized.\n", __func__);
324 return;
325 }
326
327 cec_ctrl->cec_device_suspend = suspend;
328}
329
330bool hdmi_cec_is_wakeup_en(void *input)
331{
332 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
333
334 if (!cec_ctrl) {
335 DEV_WARN("%s: HDMI CEC HW module not initialized.\n", __func__);
336 return 0;
337 }
338
339 return cec_ctrl->cec_wakeup_en;
340}
341
342static void hdmi_cec_wakeup_en(void *input, bool enable)
343{
344 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
345
346 if (!cec_ctrl) {
347 DEV_ERR("%s: Invalid input\n", __func__);
348 return;
349 }
350
351 cec_ctrl->cec_wakeup_en = enable;
352}
353
354static void hdmi_cec_write_logical_addr(void *input, u8 addr)
355{
356 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
357
358 if (!cec_ctrl || !cec_ctrl->init_data.io) {
359 DEV_ERR("%s: Invalid input\n", __func__);
360 return;
361 }
362
363 if (cec_ctrl->cec_enabled)
364 DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF);
365}
366
367static int hdmi_cec_enable(void *input, bool enable)
368{
369 int ret = 0;
370 u32 hdmi_hw_version, reg_val;
Sachin Bhayare5076e252018-01-18 14:56:45 +0530371 struct mdss_io_data *io = NULL;
Sachin Bhayareeeb88892018-01-02 16:36:01 +0530372 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
373 struct mdss_panel_info *pinfo;
374
375 if (!cec_ctrl || !cec_ctrl->init_data.io) {
376 DEV_ERR("%s: Invalid input\n", __func__);
377 ret = -EPERM;
378 goto end;
379 }
380
381 io = cec_ctrl->init_data.io;
382 pinfo = cec_ctrl->init_data.pinfo;
383
384 if (!pinfo) {
385 DEV_ERR("%s: invalid pinfo\n", __func__);
386 goto end;
387 }
388
389 if (enable) {
390 /* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */
391 DSS_REG_W(io, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16));
392
393 hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION);
394 if (hdmi_hw_version >= CEC_SUPPORTED_HW_VERSION) {
395 DSS_REG_W(io, HDMI_CEC_RD_RANGE, 0x30AB9888);
396 DSS_REG_W(io, HDMI_CEC_WR_RANGE, 0x888AA888);
397
398 DSS_REG_W(io, HDMI_CEC_RD_START_RANGE, 0x88888888);
399 DSS_REG_W(io, HDMI_CEC_RD_TOTAL_RANGE, 0x99);
400 DSS_REG_W(io, HDMI_CEC_COMPL_CTL, 0xF);
401 DSS_REG_W(io, HDMI_CEC_WR_CHECK_CONFIG, 0x4);
402 } else {
403 DEV_DBG("%s: CEC version %d is not supported.\n",
404 __func__, hdmi_hw_version);
405 ret = -EPERM;
406 goto end;
407 }
408
409 DSS_REG_W(io, HDMI_CEC_RD_FILTER, BIT(0) | (0x7FF << 4));
410 DSS_REG_W(io, HDMI_CEC_TIME, BIT(0) | ((7 * 0x30) << 7));
411
412 /* Enable CEC interrupts */
413 DSS_REG_W(io, HDMI_CEC_INT, CEC_INTR);
414
415 /* Enable Engine */
416 DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
417 } else {
418 /* Disable Engine */
419 DSS_REG_W(io, HDMI_CEC_CTRL, 0);
420
421 /* Disable CEC interrupts */
422 reg_val = DSS_REG_R(io, HDMI_CEC_INT);
423 DSS_REG_W(io, HDMI_CEC_INT, reg_val & ~CEC_INTR);
424 }
425
426 cec_ctrl->cec_enabled = enable;
427end:
428 return ret;
429}
430
431/**
432 * hdmi_cec_init() - Initialize the CEC hw module
433 * @init_data: data needed to initialize the cec hw module
434 *
435 * Return: pointer to cec hw modules data that needs to be passed when
436 * calling cec hw modules API or error code.
437 *
438 * The API registers CEC HW modules with the client and provides HW
439 * specific operations.
440 */
441void *hdmi_cec_init(struct hdmi_cec_init_data *init_data)
442{
443 struct hdmi_cec_ctrl *cec_ctrl;
444 struct cec_ops *ops;
445 int ret = 0;
446
447 if (!init_data) {
448 DEV_ERR("%s: Invalid input\n", __func__);
449 ret = -EINVAL;
450 goto error;
451 }
452
453 ops = init_data->ops;
454 if (!ops) {
455 DEV_ERR("%s: no ops provided\n", __func__);
456 ret = -EINVAL;
457 goto error;
458 }
459
460 cec_ctrl = kzalloc(sizeof(*cec_ctrl), GFP_KERNEL);
461 if (!cec_ctrl) {
462 DEV_ERR("%s: FAILED: out of memory\n", __func__);
463 ret = -EINVAL;
464 goto error;
465 }
466
467 /* keep a copy of init data */
468 cec_ctrl->init_data = *init_data;
469
470 spin_lock_init(&cec_ctrl->lock);
471 INIT_WORK(&cec_ctrl->cec_read_work, hdmi_cec_msg_recv);
472 init_completion(&cec_ctrl->cec_msg_wr_done);
473
474 /* populate hardware specific operations to client */
475 ops->send_msg = hdmi_cec_msg_send;
476 ops->wt_logical_addr = hdmi_cec_write_logical_addr;
477 ops->enable = hdmi_cec_enable;
478 ops->data = cec_ctrl;
479 ops->wakeup_en = hdmi_cec_wakeup_en;
480 ops->is_wakeup_en = hdmi_cec_is_wakeup_en;
481 ops->device_suspend = hdmi_cec_device_suspend;
482
483 hdmi_cec_init_input_event(cec_ctrl);
484
485 return cec_ctrl;
486error:
487 return ERR_PTR(ret);
488}
489
490/**
491 * hdmi_cec_deinit() - de-initialize CEC HW module
492 * @data: CEC HW module data
493 *
494 * This API release all resources allocated.
495 */
496void hdmi_cec_deinit(void *data)
497{
498 struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)data;
499
500 if (cec_ctrl)
501 hdmi_cec_deinit_input_event(cec_ctrl);
502
503 kfree(cec_ctrl);
504}