Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/**************************************************************************
  2
  3Copyright © 2006 Dave Airlie
  4
  5All Rights Reserved.
  6
  7Permission is hereby granted, free of charge, to any person obtaining a
  8copy of this software and associated documentation files (the
  9"Software"), to deal in the Software without restriction, including
 10without limitation the rights to use, copy, modify, merge, publish,
 11distribute, sub license, and/or sell copies of the Software, and to
 12permit persons to whom the Software is furnished to do so, subject to
 13the following conditions:
 14
 15The above copyright notice and this permission notice (including the
 16next paragraph) shall be included in all copies or substantial portions
 17of the Software.
 18
 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 26
 27**************************************************************************/
 28
 29#include "dvo.h"
 30
 31#define CH7xxx_REG_VID		0x4a
 32#define CH7xxx_REG_DID		0x4b
 33
 34#define CH7011_VID		0x83 /* 7010 as well */
 35#define CH7009A_VID		0x84
 36#define CH7009B_VID		0x85
 37#define CH7301_VID		0x95
 38
 39#define CH7xxx_VID		0x84
 40#define CH7xxx_DID		0x17
 41
 42#define CH7xxx_NUM_REGS		0x4c
 43
 44#define CH7xxx_CM		0x1c
 45#define CH7xxx_CM_XCM		(1<<0)
 46#define CH7xxx_CM_MCP		(1<<2)
 47#define CH7xxx_INPUT_CLOCK	0x1d
 48#define CH7xxx_GPIO		0x1e
 49#define CH7xxx_GPIO_HPIR	(1<<3)
 50#define CH7xxx_IDF		0x1f
 51
 52#define CH7xxx_IDF_HSP		(1<<3)
 53#define CH7xxx_IDF_VSP		(1<<4)
 54
 55#define CH7xxx_CONNECTION_DETECT 0x20
 56#define CH7xxx_CDET_DVI		(1<<5)
 57
 58#define CH7301_DAC_CNTL		0x21
 59#define CH7301_HOTPLUG		0x23
 60#define CH7xxx_TCTL		0x31
 61#define CH7xxx_TVCO		0x32
 62#define CH7xxx_TPCP		0x33
 63#define CH7xxx_TPD		0x34
 64#define CH7xxx_TPVT		0x35
 65#define CH7xxx_TLPF		0x36
 66#define CH7xxx_TCT		0x37
 67#define CH7301_TEST_PATTERN	0x48
 68
 69#define CH7xxx_PM		0x49
 70#define CH7xxx_PM_FPD		(1<<0)
 71#define CH7301_PM_DACPD0	(1<<1)
 72#define CH7301_PM_DACPD1	(1<<2)
 73#define CH7301_PM_DACPD2	(1<<3)
 74#define CH7xxx_PM_DVIL		(1<<6)
 75#define CH7xxx_PM_DVIP		(1<<7)
 76
 77#define CH7301_SYNC_POLARITY	0x56
 78#define CH7301_SYNC_RGB_YUV	(1<<0)
 79#define CH7301_SYNC_POL_DVI	(1<<5)
 80
 81/** @file
 82 * driver for the Chrontel 7xxx DVI chip over DVO.
 83 */
 84
 85static struct ch7xxx_id_struct {
 86	uint8_t vid;
 87	char *name;
 88} ch7xxx_ids[] = {
 89	{ CH7011_VID, "CH7011" },
 90	{ CH7009A_VID, "CH7009A" },
 91	{ CH7009B_VID, "CH7009B" },
 92	{ CH7301_VID, "CH7301" },
 93};
 94
 95struct ch7xxx_priv {
 96	bool quiet;
 97};
 98
 99static char *ch7xxx_get_id(uint8_t vid)
100{
101	int i;
102
103	for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
104		if (ch7xxx_ids[i].vid == vid)
105			return ch7xxx_ids[i].name;
106	}
107
108	return NULL;
109}
110
111/** Reads an 8 bit register */
112static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
113{
114	struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
115	struct i2c_adapter *adapter = dvo->i2c_bus;
116	u8 out_buf[2];
117	u8 in_buf[2];
118
119	struct i2c_msg msgs[] = {
120		{
121			.addr = dvo->slave_addr,
122			.flags = 0,
123			.len = 1,
124			.buf = out_buf,
125		},
126		{
127			.addr = dvo->slave_addr,
128			.flags = I2C_M_RD,
129			.len = 1,
130			.buf = in_buf,
131		}
132	};
133
134	out_buf[0] = addr;
135	out_buf[1] = 0;
136
137	if (i2c_transfer(adapter, msgs, 2) == 2) {
138		*ch = in_buf[0];
139		return true;
140	};
141
142	if (!ch7xxx->quiet) {
143		DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
144			  addr, adapter->name, dvo->slave_addr);
145	}
146	return false;
147}
148
149/** Writes an 8 bit register */
150static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
151{
152	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
153	struct i2c_adapter *adapter = dvo->i2c_bus;
154	uint8_t out_buf[2];
155	struct i2c_msg msg = {
156		.addr = dvo->slave_addr,
157		.flags = 0,
158		.len = 2,
159		.buf = out_buf,
160	};
161
162	out_buf[0] = addr;
163	out_buf[1] = ch;
164
165	if (i2c_transfer(adapter, &msg, 1) == 1)
166		return true;
167
168	if (!ch7xxx->quiet) {
169		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
170			  addr, adapter->name, dvo->slave_addr);
171	}
172
173	return false;
174}
175
176static bool ch7xxx_init(struct intel_dvo_device *dvo,
177			struct i2c_adapter *adapter)
178{
179	/* this will detect the CH7xxx chip on the specified i2c bus */
180	struct ch7xxx_priv *ch7xxx;
181	uint8_t vendor, device;
182	char *name;
183
184	ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
185	if (ch7xxx == NULL)
186		return false;
187
188	dvo->i2c_bus = adapter;
189	dvo->dev_priv = ch7xxx;
190	ch7xxx->quiet = true;
191
192	if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
193		goto out;
194
195	name = ch7xxx_get_id(vendor);
196	if (!name) {
197		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
198				"slave %d.\n",
199			  vendor, adapter->name, dvo->slave_addr);
200		goto out;
201	}
202
203
204	if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
205		goto out;
206
207	if (device != CH7xxx_DID) {
208		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
209				"slave %d.\n",
210			  vendor, adapter->name, dvo->slave_addr);
211		goto out;
212	}
213
214	ch7xxx->quiet = false;
215	DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
216		  name, vendor, device);
217	return true;
218out:
219	kfree(ch7xxx);
220	return false;
221}
222
223static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
224{
225	uint8_t cdet, orig_pm, pm;
226
227	ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
228
229	pm = orig_pm;
230	pm &= ~CH7xxx_PM_FPD;
231	pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
232
233	ch7xxx_writeb(dvo, CH7xxx_PM, pm);
234
235	ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
236
237	ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
238
239	if (cdet & CH7xxx_CDET_DVI)
240		return connector_status_connected;
241	return connector_status_disconnected;
242}
243
244static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
245					      struct drm_display_mode *mode)
246{
247	if (mode->clock > 165000)
248		return MODE_CLOCK_HIGH;
249
250	return MODE_OK;
251}
252
253static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
254			    struct drm_display_mode *mode,
255			    struct drm_display_mode *adjusted_mode)
256{
257	uint8_t tvco, tpcp, tpd, tlpf, idf;
258
259	if (mode->clock <= 65000) {
260		tvco = 0x23;
261		tpcp = 0x08;
262		tpd = 0x16;
263		tlpf = 0x60;
264	} else {
265		tvco = 0x2d;
266		tpcp = 0x06;
267		tpd = 0x26;
268		tlpf = 0xa0;
269	}
270
271	ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
272	ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
273	ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
274	ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
275	ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
276	ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
277	ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
278
279	ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
280
281	idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
282	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
283		idf |= CH7xxx_IDF_HSP;
284
285	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
286		idf |= CH7xxx_IDF_HSP;
287
288	ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
289}
290
291/* set the CH7xxx power state */
292static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
293{
294	if (mode == DRM_MODE_DPMS_ON)
295		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
296	else
297		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
298}
299
300static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
301{
302	int i;
303
304	for (i = 0; i < CH7xxx_NUM_REGS; i++) {
305		uint8_t val;
306		if ((i % 8) == 0 )
307			DRM_LOG_KMS("\n %02X: ", i);
308		ch7xxx_readb(dvo, i, &val);
309		DRM_LOG_KMS("%02X ", val);
310	}
311}
312
313static void ch7xxx_destroy(struct intel_dvo_device *dvo)
314{
315	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
316
317	if (ch7xxx) {
318		kfree(ch7xxx);
319		dvo->dev_priv = NULL;
320	}
321}
322
323struct intel_dvo_dev_ops ch7xxx_ops = {
324	.init = ch7xxx_init,
325	.detect = ch7xxx_detect,
326	.mode_valid = ch7xxx_mode_valid,
327	.mode_set = ch7xxx_mode_set,
328	.dpms = ch7xxx_dpms,
329	.dump_regs = ch7xxx_dump_regs,
330	.destroy = ch7xxx_destroy,
331};