1 /*
2  * Copyright 2017-2018, 2020 NXP
3  * All rights reserved.
4  *
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_video_common.h"
10 #include "fsl_display.h"
11 #include "fsl_adv7535.h"
12 
13 /*******************************************************************************
14  * Definitions
15  ******************************************************************************/
16 #define ADV7535_CHIP_REVISION 0x14U
17 
18 #define ADV7535_DSI_CEC_ADDR (0x78U >> 1)
19 
20 #define ADV7535_I2C_WriteReg(handle, addr, reg, value)                                \
21     VIDEO_I2C_WriteReg(addr, kVIDEO_RegAddr8Bit, (reg), kVIDEO_RegWidth8Bit, (value), \
22                        ((const adv7535_resource_t *)((handle)->resource))->i2cSendFunc)
23 
24 #define ADV7535_I2C_ReadReg(handle, addr, reg, value)                                \
25     VIDEO_I2C_ReadReg(addr, kVIDEO_RegAddr8Bit, (reg), kVIDEO_RegWidth8Bit, (value), \
26                       ((const adv7535_resource_t *)((handle)->resource))->i2cReceiveFunc)
27 
28 #define ADV7535_I2C_ModifyReg(handle, addr, reg, mask, value)                                \
29     VIDEO_I2C_ModifyReg(addr, kVIDEO_RegAddr8Bit, (reg), kVIDEO_RegWidth8Bit, mask, (value), \
30                         ((const adv7535_resource_t *)((handle)->resource))->i2cReceiveFunc,  \
31                         ((const adv7535_resource_t *)((handle)->resource))->i2cSendFunc)
32 
33 #define ADV7535_CHECK_RET(x)           \
34     do                                 \
35     {                                  \
36         status = (x);                  \
37         if (kStatus_Success != status) \
38         {                              \
39             return status;             \
40         }                              \
41     } while (false)
42 
43 typedef struct _adv7533_reg_val
44 {
45     uint8_t reg;   /* Register index. */
46     uint8_t mask;  /* Mask of the value. */
47     uint8_t value; /* Register value. */
48 } adv7533_reg_val_t;
49 
50 /*******************************************************************************
51  * Variables
52  ******************************************************************************/
53 static const adv7533_reg_val_t s_adv7533FixedRegs[] = {
54     /* Fixed registers that must be set on power up. */
55     /* 0x16[5:1] = 0b10000 */
56     {0x16U, (0x1FU << 1U), (0x10U << 1)},
57 
58     /* 0x9A = 0xE0 */
59     {0x9AU, 0xFF, 0x0E},
60 
61     /* 0xBA[7:3] = 0b01110 */
62     {0xBAU, (0x1FU << 3U), (0x0EU << 3U)},
63 
64     /* 0xDE = 0x82 */
65     {0xDEU, 0xFF, 0x82},
66 
67     /* 0xE4[6] = 1 */
68     {0xE4U, (1 << 6), (1 << 6)},
69 
70     /* 0xE5 = 0x80 */
71     {0xE5U, 0xFF, 0x80},
72 };
73 
74 static const adv7533_reg_val_t s_adv7533CecFixedRegs[] = {
75     /* CEC memory 0x15[5:4] = 1 */
76     {0x15U, (3U << 4U), (1 << 4)},
77 
78     /* CEC memory 0x17[7:4] = 0b1101 */
79     {0x17U, (0xFU << 4U), (0x0D << 4U)},
80 
81     /* CEC memory 0x24[4] = 0 */
82     {0x24U, (1 << 4U), (0x0 << 4U)},
83 
84     /* CEC memory 0x57[0] = 1 */
85     {0x57U, (1 << 0U), (0x1 << 0U)},
86 
87     /* CEC memory 0x57[4] = 1 */
88     {0x57U, (1 << 4U), (0x1 << 4U)},
89 };
90 
91 const display_operations_t adv7535_ops = {
92     .init   = ADV7535_Init,
93     .deinit = ADV7535_Deinit,
94     .start  = ADV7535_Start,
95     .stop   = ADV7535_Stop,
96 };
97 
98 /*******************************************************************************
99  * Code
100  ******************************************************************************/
101 
ADV7535_I2C_ModifyRegs(display_handle_t * handle,uint8_t i2cAddr,const adv7533_reg_val_t values[],uint32_t len)102 static status_t ADV7535_I2C_ModifyRegs(display_handle_t *handle,
103                                        uint8_t i2cAddr,
104                                        const adv7533_reg_val_t values[],
105                                        uint32_t len)
106 {
107     status_t status = kStatus_Success;
108 
109     for (uint32_t i = 0; i < len; i++)
110     {
111         status = ADV7535_I2C_ModifyReg(handle, i2cAddr, values[i].reg, values[i].mask, values[i].value);
112 
113         if (kStatus_Success != status)
114         {
115             return status;
116         }
117     }
118 
119     return status;
120 }
121 
ADV7535_Init(display_handle_t * handle,const display_config_t * config)122 status_t ADV7535_Init(display_handle_t *handle, const display_config_t *config)
123 {
124     uint8_t chipRevision = 0U;
125     uint8_t mainI2cAddr  = (((const adv7535_resource_t *)(handle->resource))->i2cAddr);
126     status_t status;
127 
128     uint8_t lanes = config->dsiLanes;
129     uint16_t total_width, total_height;
130 
131     if ((lanes <= 1U) || (lanes > 4U))
132     {
133         return kStatus_InvalidArgument;
134     }
135 
136     /* Identify the device. */
137     status = ADV7535_I2C_ReadReg(handle, mainI2cAddr, 0x00, &chipRevision);
138 
139     if (kStatus_Success != status)
140     {
141         return status;
142     }
143 
144     if (ADV7535_CHIP_REVISION != chipRevision)
145     {
146         return kStatus_Fail;
147     }
148 
149     /* -------- Power up sequence. -------- */
150     /* 0x41[6] - HDMI Power-down (Power Up = 0 HPD Must be High). */
151     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0x41, (0x01U << 6U), 0U << 6));
152 
153     /* 0xD6[6] - HPD Override (Override = 1). */
154     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0xD6, (0x01U << 6U), 1U << 6));
155 
156     /*
157      * DSI and CEC Map: 0x03[1] - Gate DSI LP Oscillator and DSI Bias Clock Powerdown, set to 0
158      * to enable normal operation
159      */
160     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x03, (0x01U << 1U), 0U << 1));
161 
162     ADV7535_CHECK_RET(ADV7535_I2C_ModifyRegs(handle, mainI2cAddr, s_adv7533FixedRegs, ARRAY_SIZE(s_adv7533FixedRegs)));
163 
164     status =
165         ADV7535_I2C_ModifyRegs(handle, ADV7535_DSI_CEC_ADDR, s_adv7533CecFixedRegs, ARRAY_SIZE(s_adv7533CecFixedRegs));
166     if (kStatus_Success != status)
167     {
168         return status;
169     }
170 
171     /* -------- Configure the video timing. --------*/
172 
173     /* Mute the HDMI. */
174     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0xd5, 1U << 0, 1U << 0));
175 
176     /* Use automatic pixel clock divider: 0x16[2] = 0 */
177     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x16, (0x01U << 2U), 0U << 2));
178 
179     /* MIPI DSI lanes: 0x1c[4:6]. */
180     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x1c, (0x07U << 4U), (uint32_t)lanes << 4));
181 
182     /* Use internal timing generator, first disable it then configure: 0x27[7] = 1 */
183     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x27, (0x01U << 7U), 1U << 7));
184 
185     /* Total width. */
186     total_width = FSL_VIDEO_EXTRACT_WIDTH(config->resolution) + config->hfp + config->hbp + config->hsw;
187     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x28, (uint8_t)(total_width >> 4U)));
188     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x29, (uint8_t)(total_width << 4U)));
189 
190     /* HSW. */
191     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2A, (uint8_t)(config->hsw >> 4U)));
192     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2B, (uint8_t)(config->hsw << 4U)));
193 
194     /* HFP. */
195     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2C, (uint8_t)(config->hfp >> 4U)));
196     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2D, (uint8_t)(config->hfp << 4U)));
197 
198     /* HBP. */
199     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2E, (uint8_t)(config->hbp >> 4U)));
200     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x2F, (uint8_t)(config->hbp << 4U)));
201 
202     /* Total height. */
203     total_height = FSL_VIDEO_EXTRACT_HEIGHT(config->resolution) + config->vfp + config->vbp + config->vsw;
204     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x30, (uint8_t)(total_height >> 4U)));
205     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x31, (uint8_t)(total_height << 4U)));
206 
207     /* VSW. */
208     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x32, (uint8_t)(config->vsw >> 4U)));
209     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x33, (uint8_t)(config->vsw << 4U)));
210 
211     /* VFP. */
212     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x34, (uint8_t)(config->vfp >> 4U)));
213     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x35, (uint8_t)(config->vfp << 4U)));
214 
215     /* VBP. */
216     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x36, (uint8_t)(config->vbp >> 4U)));
217     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x37, (uint8_t)(config->vbp << 4U)));
218 
219     /* Toggle the 0x27[6] to use the new timing parameter. */
220     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x27, (0x01U << 6U), 1U << 6));
221     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x27, (0x01U << 6U), 0U << 6));
222     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x27, (0x01U << 6U), 1U << 6));
223 
224     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0xd5, 1U << 0, 0U << 0));
225 
226     /* ----------- HDMI output. ----------- */
227     /* 0xAF[1] - HDMI/DVI Mode Select (HDMI = 1) */
228     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0xAF, (0x01U << 1U), 1U << 1));
229 
230     /* Disable the test pattern. */
231     ADV7535_CHECK_RET(ADV7535_I2C_WriteReg(handle, ADV7535_DSI_CEC_ADDR, 0x55, 0x00));
232 
233     /* 0x40[7] - GC Packet Enable (Enable = 1) */
234     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0x40, (0x01U << 7U), 1U << 7));
235 
236     /*
237      * 0x4C[3:0] Color depth of video to RX.
238      * 0000 = color depth not indicated
239      * 0100 = 24 bits per pixel
240      * 0101 = 30 bits per pixel
241      * 0110 = 36 bits per pixel
242      *
243      * Here use 24 bpp
244      */
245     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0x4C, (0x0FU << 0U), 4U << 0));
246 
247     /* 0x49[1:0] - Down Dither Output Color Depth (12 bits = 0b10) */
248     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0x49, (0x03U << 0U), 2U << 0));
249 
250     /* DSI and CEC Map: 0x03[7] - HDMI Output Enable (Enable = 1) */
251     ADV7535_CHECK_RET(ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x03, (0x01U << 7U), 1U << 7));
252 
253     return kStatus_Success;
254 }
255 
ADV7535_Deinit(display_handle_t * handle)256 status_t ADV7535_Deinit(display_handle_t *handle)
257 {
258     uint8_t mainI2cAddr = (((const adv7535_resource_t *)(handle->resource))->i2cAddr);
259 
260     /* 0x41[6] - HDMI Power-down */
261     return ADV7535_I2C_ModifyReg(handle, mainI2cAddr, 0x41, (0x01U << 6U), 1U << 6);
262 }
263 
ADV7535_Start(display_handle_t * handle)264 status_t ADV7535_Start(display_handle_t *handle)
265 {
266     /* DSI and CEC Map: 0x03[7] - HDMI Output Enable (Enable = 1) */
267     return ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x03, (0x01U << 7U), 1U << 7);
268 }
269 
ADV7535_Stop(display_handle_t * handle)270 status_t ADV7535_Stop(display_handle_t *handle)
271 {
272     /* DSI and CEC Map: 0x03[7] - HDMI Output Enable (Enable = 1) */
273     return ADV7535_I2C_ModifyReg(handle, ADV7535_DSI_CEC_ADDR, 0x03, (0x01U << 7U), 0U << 7);
274 }
275