video: rockchip: hdmi-cec: fix quick standby then wake-up crash
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmiv2 / rockchip_hdmiv2_cec.c
1 #include <linux/delay.h>
2 #include "../rockchip-hdmi-cec.h"
3 #include "rockchip_hdmiv2.h"
4 #include "rockchip_hdmiv2_hw.h"
5
6 /* static wait_queue_head_t     wait;*/
7 static int init = 1;
8 void rockchip_hdmiv2_cec_isr(struct hdmi_dev *hdmi_dev, char cec_int)
9 {
10         CECDBG("%s cec 0x%x\n", __func__, cec_int);
11         if (cec_int & m_EOM)
12                 rockchip_hdmi_cec_submit_work(EVENT_RX_FRAME, 0, NULL);
13         if (cec_int & m_DONE)
14                 CECDBG("send frame success\n");
15 }
16
17 static int rockchip_hdmiv2_cec_readframe(struct hdmi *hdmi,
18                                          struct cec_framedata *frame)
19 {
20         struct hdmi_dev *hdmi_dev = hdmi->property->priv;
21         int i, count;
22         char *data = (char *)frame;
23
24         if (((hdmi_dev->clk_on & HDMI_PCLK_ON) == 0) || !frame)
25                 return -1;
26         count = hdmi_readl(hdmi_dev, CEC_RX_CNT);
27         CECDBG("%s count %d\n", __func__, count);
28         for (i = 0; i < count; i++) {
29                 data[i] = hdmi_readl(hdmi_dev, CEC_RX_DATA0 + i);
30                 CECDBG("%02x\n", data[i]);
31         }
32         frame->argcount = count - 2;
33         hdmi_writel(hdmi_dev, CEC_LOCK, 0x0);
34         return 0;
35 }
36
37 void rockchip_hdmiv2_cec_setcecla(struct hdmi *hdmi, int ceclgaddr)
38 {
39         struct hdmi_dev *hdmi_dev = hdmi->property->priv;
40         short val;
41
42         if ((hdmi_dev->clk_on & HDMI_PCLK_ON) == 0)
43                 return;
44         if (ceclgaddr < 0 || ceclgaddr > 16)
45                 return;
46         val = 1 << ceclgaddr;
47         hdmi_writel(hdmi_dev, CEC_ADDR_L, val & 0xff);
48         hdmi_writel(hdmi_dev, CEC_ADDR_H, val >> 8);
49 }
50
51 static int rockchip_hdmiv2_cec_sendframe(struct hdmi *hdmi,
52                                          struct cec_framedata *frame)
53 {
54         struct hdmi_dev *hdmi_dev = hdmi->property->priv;
55         int i, interrupt;
56
57         if ((hdmi_dev->clk_on & HDMI_PCLK_ON) == 0)
58                 return CEC_SEND_NACK;
59         CECDBG("TX srcdestaddr %02x opcode %02x ",
60                frame->srcdestaddr, frame->opcode);
61         if (frame->argcount) {
62                 CECDBG("args:");
63                 for (i = 0; i < frame->argcount; i++)
64                         CECDBG("%02x ", frame->args[i]);
65         }
66         CECDBG("\n");
67         if ((frame->srcdestaddr & 0x0f) == ((frame->srcdestaddr >> 4) & 0x0f)) {
68                 /*it is a ping command*/
69                 hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
70                 hdmi_writel(hdmi_dev, CEC_TX_CNT, 1);
71         } else {
72                 hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
73                 hdmi_writel(hdmi_dev, CEC_TX_DATA0 + 1, frame->opcode);
74                 for (i = 0; i < frame->argcount; i++)
75                         hdmi_writel(hdmi_dev,
76                                     CEC_TX_DATA0 + 2 + i, frame->args[i]);
77                 hdmi_writel(hdmi_dev, CEC_TX_CNT, frame->argcount + 2);
78         }
79         /*Start TX*/
80         hdmi_msk_reg(hdmi_dev, CEC_CTRL, m_CEC_SEND, v_CEC_SEND(1));
81         i = 400;
82         /* time = 2.4(ms)*(1 + 16)(head + param)*11(bit)*/
83         /*11bit =  start bit(4.5ms) + data bit(2.4ms) */
84         while (i--) {
85                 usleep_range(900, 1000);
86                 interrupt = hdmi_readl(hdmi_dev, IH_CEC_STAT0);
87                 if (interrupt & (m_ERR_INITIATOR | m_ARB_LOST |
88                                         m_NACK | m_DONE)) {
89                         hdmi_writel(hdmi_dev, IH_CEC_STAT0,
90                                     interrupt & (m_ERR_INITIATOR |
91                                     m_ARB_LOST | m_NACK | m_DONE));
92                         break;
93                 }
94         }
95         CECDBG("%s interrupt 0x%02x\n", __func__, interrupt);
96         if (interrupt & m_DONE)
97                 return CEC_SEND_SUCCESS;
98         else if (interrupt & m_NACK)
99                 return CEC_SEND_NACK;
100         else
101                 return CEC_SEND_BUSY;
102 }
103
104 void rockchip_hdmiv2_cec_init(struct hdmi *hdmi)
105 {
106         struct hdmi_dev *hdmi_dev = hdmi->property->priv;
107
108         if (init) {
109                 rockchip_hdmi_cec_init(hdmi,
110                                        rockchip_hdmiv2_cec_sendframe,
111                                        rockchip_hdmiv2_cec_readframe,
112                                        rockchip_hdmiv2_cec_setcecla);
113                 init = 0;
114                 /* init_waitqueue_head(&wait); */
115         }
116         /*
117          * Enable sending all message even if sink refuse
118          * message broadcasted by us.
119          */
120         if (hdmi_dev->grf_base)
121                 regmap_write(hdmi_dev->grf_base,
122                              hdmi_dev->grf_reg_offset,
123                              (1 << hdmi_dev->grf_reg_shift) |
124                              (1 << (hdmi_dev->grf_reg_shift + 16)));
125         hdmi_writel(hdmi_dev, IH_MUTE_CEC_STAT0, m_ERR_INITIATOR |
126                         m_ARB_LOST | m_NACK | m_DONE);
127         CECDBG("%s", __func__);
128 }