xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/i915/display/dvo_tfp410.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: dvo_tfp410.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $	*/
2 
3 /*
4  * Copyright © 2007 Dave Mueller
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23  * IN THE SOFTWARE.
24  *
25  * Authors:
26  *    Dave Mueller <dave.mueller@gmx.ch>
27  *
28  */
29 
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: dvo_tfp410.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $");
32 
33 #include "intel_display_types.h"
34 #include "intel_dvo_dev.h"
35 
36 /* register definitions according to the TFP410 data sheet */
37 #define TFP410_VID		0x014C
38 #define TFP410_DID		0x0410
39 
40 #define TFP410_VID_LO		0x00
41 #define TFP410_VID_HI		0x01
42 #define TFP410_DID_LO		0x02
43 #define TFP410_DID_HI		0x03
44 #define TFP410_REV		0x04
45 
46 #define TFP410_CTL_1		0x08
47 #define TFP410_CTL_1_TDIS	(1<<6)
48 #define TFP410_CTL_1_VEN	(1<<5)
49 #define TFP410_CTL_1_HEN	(1<<4)
50 #define TFP410_CTL_1_DSEL	(1<<3)
51 #define TFP410_CTL_1_BSEL	(1<<2)
52 #define TFP410_CTL_1_EDGE	(1<<1)
53 #define TFP410_CTL_1_PD		(1<<0)
54 
55 #define TFP410_CTL_2		0x09
56 #define TFP410_CTL_2_VLOW	(1<<7)
57 #define TFP410_CTL_2_MSEL_MASK	(0x7<<4)
58 #define TFP410_CTL_2_MSEL	(1<<4)
59 #define TFP410_CTL_2_TSEL	(1<<3)
60 #define TFP410_CTL_2_RSEN	(1<<2)
61 #define TFP410_CTL_2_HTPLG	(1<<1)
62 #define TFP410_CTL_2_MDI	(1<<0)
63 
64 #define TFP410_CTL_3		0x0A
65 #define TFP410_CTL_3_DK_MASK	(0x7<<5)
66 #define TFP410_CTL_3_DK		(1<<5)
67 #define TFP410_CTL_3_DKEN	(1<<4)
68 #define TFP410_CTL_3_CTL_MASK	(0x7<<1)
69 #define TFP410_CTL_3_CTL	(1<<1)
70 
71 #define TFP410_USERCFG		0x0B
72 
73 #define TFP410_DE_DLY		0x32
74 
75 #define TFP410_DE_CTL		0x33
76 #define TFP410_DE_CTL_DEGEN	(1<<6)
77 #define TFP410_DE_CTL_VSPOL	(1<<5)
78 #define TFP410_DE_CTL_HSPOL	(1<<4)
79 #define TFP410_DE_CTL_DEDLY8	(1<<0)
80 
81 #define TFP410_DE_TOP		0x34
82 
83 #define TFP410_DE_CNT_LO	0x36
84 #define TFP410_DE_CNT_HI	0x37
85 
86 #define TFP410_DE_LIN_LO	0x38
87 #define TFP410_DE_LIN_HI	0x39
88 
89 #define TFP410_H_RES_LO		0x3A
90 #define TFP410_H_RES_HI		0x3B
91 
92 #define TFP410_V_RES_LO		0x3C
93 #define TFP410_V_RES_HI		0x3D
94 
95 struct tfp410_priv {
96 	bool quiet;
97 };
98 
tfp410_readb(struct intel_dvo_device * dvo,int addr,u8 * ch)99 static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, u8 *ch)
100 {
101 	struct tfp410_priv *tfp = dvo->dev_priv;
102 	struct i2c_adapter *adapter = dvo->i2c_bus;
103 	u8 out_buf[2];
104 	u8 in_buf[2];
105 
106 	struct i2c_msg msgs[] = {
107 		{
108 			.addr = dvo->slave_addr,
109 			.flags = 0,
110 			.len = 1,
111 			.buf = out_buf,
112 		},
113 		{
114 			.addr = dvo->slave_addr,
115 			.flags = I2C_M_RD,
116 			.len = 1,
117 			.buf = in_buf,
118 		}
119 	};
120 
121 	out_buf[0] = addr;
122 	out_buf[1] = 0;
123 
124 	if (i2c_transfer(adapter, msgs, 2) == 2) {
125 		*ch = in_buf[0];
126 		return true;
127 	}
128 
129 	if (!tfp->quiet) {
130 		DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
131 			  addr, adapter->name, dvo->slave_addr);
132 	}
133 	return false;
134 }
135 
tfp410_writeb(struct intel_dvo_device * dvo,int addr,u8 ch)136 static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, u8 ch)
137 {
138 	struct tfp410_priv *tfp = dvo->dev_priv;
139 	struct i2c_adapter *adapter = dvo->i2c_bus;
140 	u8 out_buf[2];
141 	struct i2c_msg msg = {
142 		.addr = dvo->slave_addr,
143 		.flags = 0,
144 		.len = 2,
145 		.buf = out_buf,
146 	};
147 
148 	out_buf[0] = addr;
149 	out_buf[1] = ch;
150 
151 	if (i2c_transfer(adapter, &msg, 1) == 1)
152 		return true;
153 
154 	if (!tfp->quiet) {
155 		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
156 			  addr, adapter->name, dvo->slave_addr);
157 	}
158 
159 	return false;
160 }
161 
tfp410_getid(struct intel_dvo_device * dvo,int addr)162 static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
163 {
164 	u8 ch1, ch2;
165 
166 	if (tfp410_readb(dvo, addr+0, &ch1) &&
167 	    tfp410_readb(dvo, addr+1, &ch2))
168 		return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
169 
170 	return -1;
171 }
172 
173 /* Ti TFP410 driver for chip on i2c bus */
tfp410_init(struct intel_dvo_device * dvo,struct i2c_adapter * adapter)174 static bool tfp410_init(struct intel_dvo_device *dvo,
175 			struct i2c_adapter *adapter)
176 {
177 	/* this will detect the tfp410 chip on the specified i2c bus */
178 	struct tfp410_priv *tfp;
179 	int id;
180 
181 	tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL);
182 	if (tfp == NULL)
183 		return false;
184 
185 	dvo->i2c_bus = adapter;
186 	dvo->dev_priv = tfp;
187 	tfp->quiet = true;
188 
189 	if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
190 		DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
191 				"Slave %d.\n",
192 			  id, adapter->name, dvo->slave_addr);
193 		goto out;
194 	}
195 
196 	if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
197 		DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
198 				"Slave %d.\n",
199 			  id, adapter->name, dvo->slave_addr);
200 		goto out;
201 	}
202 	tfp->quiet = false;
203 	return true;
204 out:
205 	kfree(tfp);
206 	return false;
207 }
208 
tfp410_detect(struct intel_dvo_device * dvo)209 static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
210 {
211 	enum drm_connector_status ret = connector_status_disconnected;
212 	u8 ctl2;
213 
214 	if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
215 		if (ctl2 & TFP410_CTL_2_RSEN)
216 			ret = connector_status_connected;
217 		else
218 			ret = connector_status_disconnected;
219 	}
220 
221 	return ret;
222 }
223 
tfp410_mode_valid(struct intel_dvo_device * dvo,struct drm_display_mode * mode)224 static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
225 					      struct drm_display_mode *mode)
226 {
227 	return MODE_OK;
228 }
229 
tfp410_mode_set(struct intel_dvo_device * dvo,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)230 static void tfp410_mode_set(struct intel_dvo_device *dvo,
231 			    const struct drm_display_mode *mode,
232 			    const struct drm_display_mode *adjusted_mode)
233 {
234 	/* As long as the basics are set up, since we don't have clock dependencies
235 	* in the mode setup, we can just leave the registers alone and everything
236 	* will work fine.
237 	*/
238 	/* don't do much */
239 	return;
240 }
241 
242 /* set the tfp410 power state */
tfp410_dpms(struct intel_dvo_device * dvo,bool enable)243 static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable)
244 {
245 	u8 ctl1;
246 
247 	if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
248 		return;
249 
250 	if (enable)
251 		ctl1 |= TFP410_CTL_1_PD;
252 	else
253 		ctl1 &= ~TFP410_CTL_1_PD;
254 
255 	tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
256 }
257 
tfp410_get_hw_state(struct intel_dvo_device * dvo)258 static bool tfp410_get_hw_state(struct intel_dvo_device *dvo)
259 {
260 	u8 ctl1;
261 
262 	if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
263 		return false;
264 
265 	if (ctl1 & TFP410_CTL_1_PD)
266 		return true;
267 	else
268 		return false;
269 }
270 
tfp410_dump_regs(struct intel_dvo_device * dvo)271 static void tfp410_dump_regs(struct intel_dvo_device *dvo)
272 {
273 	u8 val, val2;
274 
275 	tfp410_readb(dvo, TFP410_REV, &val);
276 	DRM_DEBUG_KMS("TFP410_REV: 0x%02X\n", val);
277 	tfp410_readb(dvo, TFP410_CTL_1, &val);
278 	DRM_DEBUG_KMS("TFP410_CTL1: 0x%02X\n", val);
279 	tfp410_readb(dvo, TFP410_CTL_2, &val);
280 	DRM_DEBUG_KMS("TFP410_CTL2: 0x%02X\n", val);
281 	tfp410_readb(dvo, TFP410_CTL_3, &val);
282 	DRM_DEBUG_KMS("TFP410_CTL3: 0x%02X\n", val);
283 	tfp410_readb(dvo, TFP410_USERCFG, &val);
284 	DRM_DEBUG_KMS("TFP410_USERCFG: 0x%02X\n", val);
285 	tfp410_readb(dvo, TFP410_DE_DLY, &val);
286 	DRM_DEBUG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
287 	tfp410_readb(dvo, TFP410_DE_CTL, &val);
288 	DRM_DEBUG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
289 	tfp410_readb(dvo, TFP410_DE_TOP, &val);
290 	DRM_DEBUG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
291 	tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
292 	tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
293 	DRM_DEBUG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
294 	tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
295 	tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
296 	DRM_DEBUG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
297 	tfp410_readb(dvo, TFP410_H_RES_LO, &val);
298 	tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
299 	DRM_DEBUG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
300 	tfp410_readb(dvo, TFP410_V_RES_LO, &val);
301 	tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
302 	DRM_DEBUG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
303 }
304 
tfp410_destroy(struct intel_dvo_device * dvo)305 static void tfp410_destroy(struct intel_dvo_device *dvo)
306 {
307 	struct tfp410_priv *tfp = dvo->dev_priv;
308 
309 	if (tfp) {
310 		kfree(tfp);
311 		dvo->dev_priv = NULL;
312 	}
313 }
314 
315 const struct intel_dvo_dev_ops tfp410_ops = {
316 	.init = tfp410_init,
317 	.detect = tfp410_detect,
318 	.mode_valid = tfp410_mode_valid,
319 	.mode_set = tfp410_mode_set,
320 	.dpms = tfp410_dpms,
321 	.get_hw_state = tfp410_get_hw_state,
322 	.dump_regs = tfp410_dump_regs,
323 	.destroy = tfp410_destroy,
324 };
325