aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i2c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2009-12-11 04:24:15 -0500
committerDave Airlie <airlied@redhat.com>2009-12-11 06:29:34 -0500
commit6ee738610f41b59733f63718f0bdbcba7d3a3f12 (patch)
treeeccb9f07671998c50a1bc606a54cd6f82ba43e0a /drivers/gpu/drm/i2c
parentd1ede145cea25c5b6d2ebb19b167af14e374bb45 (diff)
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Koƛcielnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i2c')
-rw-r--r--drivers/gpu/drm/i2c/Makefile4
-rw-r--r--drivers/gpu/drm/i2c/ch7006_drv.c531
-rw-r--r--drivers/gpu/drm/i2c/ch7006_mode.c473
-rw-r--r--drivers/gpu/drm/i2c/ch7006_priv.h344
4 files changed, 1352 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
new file mode 100644
index 000000000000..6d2abaf35ba2
--- /dev/null
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -0,0 +1,4 @@
1ccflags-y := -Iinclude/drm
2
3ch7006-y := ch7006_drv.o ch7006_mode.o
4obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
new file mode 100644
index 000000000000..9422a74c8b54
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -0,0 +1,531 @@
1/*
2 * Copyright (C) 2009 Francisco Jerez.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "ch7006_priv.h"
28
29/* DRM encoder functions */
30
31static void ch7006_encoder_set_config(struct drm_encoder *encoder,
32 void *params)
33{
34 struct ch7006_priv *priv = to_ch7006_priv(encoder);
35
36 priv->params = params;
37}
38
39static void ch7006_encoder_destroy(struct drm_encoder *encoder)
40{
41 struct ch7006_priv *priv = to_ch7006_priv(encoder);
42
43 drm_property_destroy(encoder->dev, priv->scale_property);
44
45 kfree(priv);
46 to_encoder_slave(encoder)->slave_priv = NULL;
47
48 drm_i2c_encoder_destroy(encoder);
49}
50
51static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
52{
53 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
54 struct ch7006_priv *priv = to_ch7006_priv(encoder);
55 struct ch7006_state *state = &priv->state;
56
57 ch7006_dbg(client, "\n");
58
59 if (mode == priv->last_dpms)
60 return;
61 priv->last_dpms = mode;
62
63 ch7006_setup_power_state(encoder);
64
65 ch7006_load_reg(client, state, CH7006_POWER);
66}
67
68static void ch7006_encoder_save(struct drm_encoder *encoder)
69{
70 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
71 struct ch7006_priv *priv = to_ch7006_priv(encoder);
72
73 ch7006_dbg(client, "\n");
74
75 ch7006_state_save(client, &priv->saved_state);
76}
77
78static void ch7006_encoder_restore(struct drm_encoder *encoder)
79{
80 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
81 struct ch7006_priv *priv = to_ch7006_priv(encoder);
82
83 ch7006_dbg(client, "\n");
84
85 ch7006_state_load(client, &priv->saved_state);
86}
87
88static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
89 struct drm_display_mode *mode,
90 struct drm_display_mode *adjusted_mode)
91{
92 struct ch7006_priv *priv = to_ch7006_priv(encoder);
93
94 /* The ch7006 is painfully picky with the input timings so no
95 * custom modes for now... */
96
97 priv->mode = ch7006_lookup_mode(encoder, mode);
98
99 return !!priv->mode;
100}
101
102static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
103 struct drm_display_mode *mode)
104{
105 if (ch7006_lookup_mode(encoder, mode))
106 return MODE_OK;
107 else
108 return MODE_BAD;
109}
110
111static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
112 struct drm_display_mode *drm_mode,
113 struct drm_display_mode *adjusted_mode)
114{
115 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
116 struct ch7006_priv *priv = to_ch7006_priv(encoder);
117 struct ch7006_encoder_params *params = priv->params;
118 struct ch7006_state *state = &priv->state;
119 uint8_t *regs = state->regs;
120 struct ch7006_mode *mode = priv->mode;
121 struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
122 int start_active;
123
124 ch7006_dbg(client, "\n");
125
126 regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
127 regs[CH7006_BWIDTH] = 0;
128 regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
129 params->input_format);
130
131 regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
132 | bitf(CH7006_CLKMODE_XCM, params->xcm)
133 | bitf(CH7006_CLKMODE_PCM, params->pcm);
134 if (params->clock_mode)
135 regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
136 if (params->clock_edge)
137 regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
138
139 start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
140 regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
141 regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
142
143 regs[CH7006_INPUT_SYNC] = 0;
144 if (params->sync_direction)
145 regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
146 if (params->sync_encoding)
147 regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
148 if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
149 regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
150 if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
151 regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
152
153 regs[CH7006_DETECT] = 0;
154 regs[CH7006_BCLKOUT] = 0;
155
156 regs[CH7006_SUBC_INC3] = 0;
157 if (params->pout_level)
158 regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
159
160 regs[CH7006_SUBC_INC4] = 0;
161 if (params->active_detect)
162 regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
163
164 regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
165
166 ch7006_setup_levels(encoder);
167 ch7006_setup_subcarrier(encoder);
168 ch7006_setup_pll(encoder);
169 ch7006_setup_power_state(encoder);
170 ch7006_setup_properties(encoder);
171
172 ch7006_state_load(client, state);
173}
174
175static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
176 struct drm_connector *connector)
177{
178 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
179 struct ch7006_priv *priv = to_ch7006_priv(encoder);
180 struct ch7006_state *state = &priv->state;
181 int det;
182
183 ch7006_dbg(client, "\n");
184
185 ch7006_save_reg(client, state, CH7006_DETECT);
186 ch7006_save_reg(client, state, CH7006_POWER);
187 ch7006_save_reg(client, state, CH7006_CLKMODE);
188
189 ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
190 bitfs(CH7006_POWER_LEVEL, NORMAL));
191 ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
192
193 ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
194
195 ch7006_write(client, CH7006_DETECT, 0);
196
197 det = ch7006_read(client, CH7006_DETECT);
198
199 ch7006_load_reg(client, state, CH7006_CLKMODE);
200 ch7006_load_reg(client, state, CH7006_POWER);
201 ch7006_load_reg(client, state, CH7006_DETECT);
202
203 if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
204 CH7006_DETECT_SVIDEO_C_TEST|
205 CH7006_DETECT_CVBS_TEST)) == 0)
206 priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
207 else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
208 CH7006_DETECT_SVIDEO_C_TEST)) == 0)
209 priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
210 else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
211 priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
212 else
213 priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
214
215 drm_connector_property_set_value(connector,
216 encoder->dev->mode_config.tv_subconnector_property,
217 priv->subconnector);
218
219 return priv->subconnector ? connector_status_connected :
220 connector_status_disconnected;
221}
222
223static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
224 struct drm_connector *connector)
225{
226 struct ch7006_priv *priv = to_ch7006_priv(encoder);
227 struct ch7006_mode *mode;
228 int n = 0;
229
230 for (mode = ch7006_modes; mode->mode.clock; mode++) {
231 if (~mode->valid_scales & 1<<priv->scale ||
232 ~mode->valid_norms & 1<<priv->norm)
233 continue;
234
235 drm_mode_probed_add(connector,
236 drm_mode_duplicate(encoder->dev, &mode->mode));
237
238 n++;
239 }
240
241 return n;
242}
243
244static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
245 struct drm_connector *connector)
246{
247 struct ch7006_priv *priv = to_ch7006_priv(encoder);
248 struct drm_device *dev = encoder->dev;
249 struct drm_mode_config *conf = &dev->mode_config;
250
251 drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
252
253 priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
254 "scale", 2);
255 priv->scale_property->values[0] = 0;
256 priv->scale_property->values[1] = 2;
257
258 drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
259 priv->select_subconnector);
260 drm_connector_attach_property(connector, conf->tv_subconnector_property,
261 priv->subconnector);
262 drm_connector_attach_property(connector, conf->tv_left_margin_property,
263 priv->hmargin);
264 drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
265 priv->vmargin);
266 drm_connector_attach_property(connector, conf->tv_mode_property,
267 priv->norm);
268 drm_connector_attach_property(connector, conf->tv_brightness_property,
269 priv->brightness);
270 drm_connector_attach_property(connector, conf->tv_contrast_property,
271 priv->contrast);
272 drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
273 priv->flicker);
274 drm_connector_attach_property(connector, priv->scale_property,
275 priv->scale);
276
277 return 0;
278}
279
280static int ch7006_encoder_set_property(struct drm_encoder *encoder,
281 struct drm_connector *connector,
282 struct drm_property *property,
283 uint64_t val)
284{
285 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
286 struct ch7006_priv *priv = to_ch7006_priv(encoder);
287 struct ch7006_state *state = &priv->state;
288 struct drm_mode_config *conf = &encoder->dev->mode_config;
289 struct drm_crtc *crtc = encoder->crtc;
290 bool modes_changed = false;
291
292 ch7006_dbg(client, "\n");
293
294 if (property == conf->tv_select_subconnector_property) {
295 priv->select_subconnector = val;
296
297 ch7006_setup_power_state(encoder);
298
299 ch7006_load_reg(client, state, CH7006_POWER);
300
301 } else if (property == conf->tv_left_margin_property) {
302 priv->hmargin = val;
303
304 ch7006_setup_properties(encoder);
305
306 ch7006_load_reg(client, state, CH7006_POV);
307 ch7006_load_reg(client, state, CH7006_HPOS);
308
309 } else if (property == conf->tv_bottom_margin_property) {
310 priv->vmargin = val;
311
312 ch7006_setup_properties(encoder);
313
314 ch7006_load_reg(client, state, CH7006_POV);
315 ch7006_load_reg(client, state, CH7006_VPOS);
316
317 } else if (property == conf->tv_mode_property) {
318 if (connector->dpms != DRM_MODE_DPMS_OFF)
319 return -EINVAL;
320
321 priv->norm = val;
322
323 modes_changed = true;
324
325 } else if (property == conf->tv_brightness_property) {
326 priv->brightness = val;
327
328 ch7006_setup_levels(encoder);
329
330 ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
331
332 } else if (property == conf->tv_contrast_property) {
333 priv->contrast = val;
334
335 ch7006_setup_properties(encoder);
336
337 ch7006_load_reg(client, state, CH7006_CONTRAST);
338
339 } else if (property == conf->tv_flicker_reduction_property) {
340 priv->flicker = val;
341
342 ch7006_setup_properties(encoder);
343
344 ch7006_load_reg(client, state, CH7006_FFILTER);
345
346 } else if (property == priv->scale_property) {
347 if (connector->dpms != DRM_MODE_DPMS_OFF)
348 return -EINVAL;
349
350 priv->scale = val;
351
352 modes_changed = true;
353
354 } else {
355 return -EINVAL;
356 }
357
358 if (modes_changed) {
359 drm_helper_probe_single_connector_modes(connector, 0, 0);
360
361 /* Disable the crtc to ensure a full modeset is
362 * performed whenever it's turned on again. */
363 if (crtc) {
364 struct drm_mode_set modeset = {
365 .crtc = crtc,
366 };
367
368 crtc->funcs->set_config(&modeset);
369 }
370 }
371
372 return 0;
373}
374
375static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
376 .set_config = ch7006_encoder_set_config,
377 .destroy = ch7006_encoder_destroy,
378 .dpms = ch7006_encoder_dpms,
379 .save = ch7006_encoder_save,
380 .restore = ch7006_encoder_restore,
381 .mode_fixup = ch7006_encoder_mode_fixup,
382 .mode_valid = ch7006_encoder_mode_valid,
383 .mode_set = ch7006_encoder_mode_set,
384 .detect = ch7006_encoder_detect,
385 .get_modes = ch7006_encoder_get_modes,
386 .create_resources = ch7006_encoder_create_resources,
387 .set_property = ch7006_encoder_set_property,
388};
389
390
391/* I2C driver functions */
392
393static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
394{
395 uint8_t addr = CH7006_VERSION_ID;
396 uint8_t val;
397 int ret;
398
399 ch7006_dbg(client, "\n");
400
401 ret = i2c_master_send(client, &addr, sizeof(addr));
402 if (ret < 0)
403 goto fail;
404
405 ret = i2c_master_recv(client, &val, sizeof(val));
406 if (ret < 0)
407 goto fail;
408
409 ch7006_info(client, "Detected version ID: %x\n", val);
410
411 return 0;
412
413fail:
414 ch7006_err(client, "Error %d reading version ID\n", ret);
415
416 return -ENODEV;
417}
418
419static int ch7006_remove(struct i2c_client *client)
420{
421 ch7006_dbg(client, "\n");
422
423 return 0;
424}
425
426static int ch7006_encoder_init(struct i2c_client *client,
427 struct drm_device *dev,
428 struct drm_encoder_slave *encoder)
429{
430 struct ch7006_priv *priv;
431 int i;
432
433 ch7006_dbg(client, "\n");
434
435 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
436 if (!priv)
437 return -ENOMEM;
438
439 encoder->slave_priv = priv;
440 encoder->slave_funcs = &ch7006_encoder_funcs;
441
442 priv->norm = TV_NORM_PAL;
443 priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
444 priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
445 priv->scale = 1;
446 priv->contrast = 50;
447 priv->brightness = 50;
448 priv->flicker = 50;
449 priv->hmargin = 50;
450 priv->vmargin = 50;
451 priv->last_dpms = -1;
452
453 if (ch7006_tv_norm) {
454 for (i = 0; i < NUM_TV_NORMS; i++) {
455 if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
456 priv->norm = i;
457 break;
458 }
459 }
460
461 if (i == NUM_TV_NORMS)
462 ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
463 ch7006_tv_norm);
464 }
465
466 if (ch7006_scale >= 0 && ch7006_scale <= 2)
467 priv->scale = ch7006_scale;
468 else
469 ch7006_err(client, "Invalid scale setting \"%d\".\n",
470 ch7006_scale);
471
472 return 0;
473}
474
475static struct i2c_device_id ch7006_ids[] = {
476 { "ch7006", 0 },
477 { }
478};
479MODULE_DEVICE_TABLE(i2c, ch7006_ids);
480
481static struct drm_i2c_encoder_driver ch7006_driver = {
482 .i2c_driver = {
483 .probe = ch7006_probe,
484 .remove = ch7006_remove,
485
486 .driver = {
487 .name = "ch7006",
488 },
489
490 .id_table = ch7006_ids,
491 },
492
493 .encoder_init = ch7006_encoder_init,
494};
495
496
497/* Module initialization */
498
499static int __init ch7006_init(void)
500{
501 return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
502}
503
504static void __exit ch7006_exit(void)
505{
506 drm_i2c_encoder_unregister(&ch7006_driver);
507}
508
509int ch7006_debug;
510module_param_named(debug, ch7006_debug, int, 0600);
511MODULE_PARM_DESC(debug, "Enable debug output.");
512
513char *ch7006_tv_norm;
514module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
515MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
516 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
517 "\t\tDefault: PAL");
518
519int ch7006_scale = 1;
520module_param_named(scale, ch7006_scale, int, 0600);
521MODULE_PARM_DESC(scale, "Default scale.\n"
522 "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
523 "\t\t\t1 -> Select default video modes.\n"
524 "\t\t\t2 -> Select video modes with a lower blanking ratio.");
525
526MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
527MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
528MODULE_LICENSE("GPL and additional rights");
529
530module_init(ch7006_init);
531module_exit(ch7006_exit);
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
new file mode 100644
index 000000000000..87f5445092e8
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
@@ -0,0 +1,473 @@
1/*
2 * Copyright (C) 2009 Francisco Jerez.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "ch7006_priv.h"
28
29char *ch7006_tv_norm_names[] = {
30 [TV_NORM_PAL] = "PAL",
31 [TV_NORM_PAL_M] = "PAL-M",
32 [TV_NORM_PAL_N] = "PAL-N",
33 [TV_NORM_PAL_NC] = "PAL-Nc",
34 [TV_NORM_PAL_60] = "PAL-60",
35 [TV_NORM_NTSC_M] = "NTSC-M",
36 [TV_NORM_NTSC_J] = "NTSC-J",
37};
38
39#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
40 .vdisplay = 480, \
41 .vtotal = 525, \
42 .hvirtual = 660
43
44#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
45 .vdisplay = 576, \
46 .vtotal = 625, \
47 .hvirtual = 810
48
49struct ch7006_tv_norm_info ch7006_tv_norms[] = {
50 [TV_NORM_NTSC_M] = {
51 NTSC_LIKE_TIMINGS,
52 .black_level = 0.339 * fixed1,
53 .subc_freq = 3579545 * fixed1,
54 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
55 .voffset = 0,
56 },
57 [TV_NORM_NTSC_J] = {
58 NTSC_LIKE_TIMINGS,
59 .black_level = 0.286 * fixed1,
60 .subc_freq = 3579545 * fixed1,
61 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
62 .voffset = 0,
63 },
64 [TV_NORM_PAL] = {
65 PAL_LIKE_TIMINGS,
66 .black_level = 0.3 * fixed1,
67 .subc_freq = 4433618.75 * fixed1,
68 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
69 .voffset = 0,
70 },
71 [TV_NORM_PAL_M] = {
72 NTSC_LIKE_TIMINGS,
73 .black_level = 0.339 * fixed1,
74 .subc_freq = 3575611.433 * fixed1,
75 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
76 .voffset = 16,
77 },
78
79 /* The following modes seem to work right but they're
80 * undocumented */
81
82 [TV_NORM_PAL_N] = {
83 PAL_LIKE_TIMINGS,
84 .black_level = 0.339 * fixed1,
85 .subc_freq = 4433618.75 * fixed1,
86 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
87 .voffset = 0,
88 },
89 [TV_NORM_PAL_NC] = {
90 PAL_LIKE_TIMINGS,
91 .black_level = 0.3 * fixed1,
92 .subc_freq = 3582056.25 * fixed1,
93 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
94 .voffset = 0,
95 },
96 [TV_NORM_PAL_60] = {
97 NTSC_LIKE_TIMINGS,
98 .black_level = 0.3 * fixed1,
99 .subc_freq = 4433618.75 * fixed1,
100 .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
101 .voffset = 16,
102 },
103};
104
105#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
106 subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
107 .mode = { \
108 .name = #hd "x" #vd, \
109 .status = 0, \
110 .type = DRM_MODE_TYPE_DRIVER, \
111 .clock = f, \
112 .hdisplay = hd, \
113 .hsync_start = e_hd + 16, \
114 .hsync_end = e_hd + 80, \
115 .htotal = ht, \
116 .hskew = 0, \
117 .vdisplay = vd, \
118 .vsync_start = vd + 10, \
119 .vsync_end = vd + 26, \
120 .vtotal = vt, \
121 .vscan = 0, \
122 .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
123 DRM_MODE_FLAG_##vsynp##VSYNC, \
124 .vrefresh = 0, \
125 }, \
126 .enc_hdisp = e_hd, \
127 .enc_vdisp = e_vd, \
128 .subc_coeff = subc * fixed1, \
129 .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
130 bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
131 .valid_scales = scale_mask, \
132 .valid_norms = norm_mask \
133 }
134
135#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
136 subc, scale, scale_mask, norm_mask) \
137 __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
138 scale_mask, norm_mask, hd, vd)
139
140#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
141 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
142
143#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
144
145struct ch7006_mode ch7006_modes[] = {
146 MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
147 MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
148 MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
149 MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
150 MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
151 MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
152 MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
153 MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
154 MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
155 MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
156 MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
157 MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
158 MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
159 MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
160 MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
161 MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
162 MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
163 MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
164 MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
165 __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
166 MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
167 MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
168 MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
169 MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
170 MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
171 {}
172};
173
174struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
175 struct drm_display_mode *drm_mode)
176{
177 struct ch7006_priv *priv = to_ch7006_priv(encoder);
178 struct ch7006_mode *mode;
179
180 for (mode = ch7006_modes; mode->mode.clock; mode++) {
181
182 if (~mode->valid_norms & 1<<priv->norm)
183 continue;
184
185 if (mode->mode.hdisplay != drm_mode->hdisplay ||
186 mode->mode.vdisplay != drm_mode->vdisplay ||
187 mode->mode.vtotal != drm_mode->vtotal ||
188 mode->mode.htotal != drm_mode->htotal ||
189 mode->mode.clock != drm_mode->clock)
190 continue;
191
192 return mode;
193 }
194
195 return NULL;
196}
197
198/* Some common HW state calculation code */
199
200void ch7006_setup_levels(struct drm_encoder *encoder)
201{
202 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
203 struct ch7006_priv *priv = to_ch7006_priv(encoder);
204 uint8_t *regs = priv->state.regs;
205 struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
206 int gain;
207 int black_level;
208
209 /* Set DAC_GAIN if the voltage drop between white and black is
210 * high enough. */
211 if (norm->black_level < 339*fixed1/1000) {
212 gain = 76;
213
214 regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
215 } else {
216 gain = 71;
217
218 regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
219 }
220
221 black_level = round_fixed(norm->black_level*26625)/gain;
222
223 /* Correct it with the specified brightness. */
224 black_level = interpolate(90, black_level, 208, priv->brightness);
225
226 regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
227
228 ch7006_dbg(client, "black level: %d\n", black_level);
229}
230
231void ch7006_setup_subcarrier(struct drm_encoder *encoder)
232{
233 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
234 struct ch7006_priv *priv = to_ch7006_priv(encoder);
235 struct ch7006_state *state = &priv->state;
236 struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
237 struct ch7006_mode *mode = priv->mode;
238 uint32_t subc_inc;
239
240 subc_inc = round_fixed((mode->subc_coeff >> 8)
241 * (norm->subc_freq >> 24));
242
243 setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
244 setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
245 setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
246 setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
247 setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
248 setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
249 setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
250 setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
251
252 ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
253}
254
255void ch7006_setup_pll(struct drm_encoder *encoder)
256{
257 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
258 struct ch7006_priv *priv = to_ch7006_priv(encoder);
259 uint8_t *regs = priv->state.regs;
260 struct ch7006_mode *mode = priv->mode;
261 int n, best_n = 0;
262 int m, best_m = 0;
263 int freq, best_freq = 0;
264
265 for (n = 0; n < CH7006_MAXN; n++) {
266 for (m = 0; m < CH7006_MAXM; m++) {
267 freq = CH7006_FREQ0*(n+2)/(m+2);
268
269 if (abs(freq - mode->mode.clock) <
270 abs(best_freq - mode->mode.clock)) {
271 best_freq = freq;
272 best_n = n;
273 best_m = m;
274 }
275 }
276 }
277
278 regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
279 bitf(CH7006_PLLOV_M_8, best_m);
280
281 regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
282 regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
283
284 if (best_n < 108)
285 regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
286 else
287 regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
288
289 ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
290 best_n, best_m, best_freq, best_n < 108);
291}
292
293void ch7006_setup_power_state(struct drm_encoder *encoder)
294{
295 struct ch7006_priv *priv = to_ch7006_priv(encoder);
296 uint8_t *power = &priv->state.regs[CH7006_POWER];
297 int subconnector;
298
299 subconnector = priv->select_subconnector ? priv->select_subconnector :
300 priv->subconnector;
301
302 *power = CH7006_POWER_RESET;
303
304 if (priv->last_dpms == DRM_MODE_DPMS_ON) {
305 switch (subconnector) {
306 case DRM_MODE_SUBCONNECTOR_SVIDEO:
307 *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
308 break;
309 case DRM_MODE_SUBCONNECTOR_Composite:
310 *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
311 break;
312 case DRM_MODE_SUBCONNECTOR_SCART:
313 *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
314 CH7006_POWER_SCART;
315 break;
316 }
317
318 } else {
319 *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
320 }
321}
322
323void ch7006_setup_properties(struct drm_encoder *encoder)
324{
325 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
326 struct ch7006_priv *priv = to_ch7006_priv(encoder);
327 struct ch7006_state *state = &priv->state;
328 struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
329 struct ch7006_mode *ch_mode = priv->mode;
330 struct drm_display_mode *mode = &ch_mode->mode;
331 uint8_t *regs = state->regs;
332 int flicker, contrast, hpos, vpos;
333 uint64_t scale, aspect;
334
335 flicker = interpolate(0, 2, 3, priv->flicker);
336 regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
337 bitf(CH7006_FFILTER_LUMA, flicker) |
338 bitf(CH7006_FFILTER_CHROMA, 1);
339
340 contrast = interpolate(0, 5, 7, priv->contrast);
341 regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
342
343 scale = norm->vtotal*fixed1;
344 do_div(scale, mode->vtotal);
345
346 aspect = ch_mode->enc_hdisp*fixed1;
347 do_div(aspect, ch_mode->enc_vdisp);
348
349 hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
350 * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
351
352 setbitf(state, CH7006_POV, HPOS_8, hpos);
353 setbitf(state, CH7006_HPOS, 0, hpos);
354
355 vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
356 + norm->voffset) * priv->vmargin / 100 / 2;
357
358 setbitf(state, CH7006_POV, VPOS_8, vpos);
359 setbitf(state, CH7006_VPOS, 0, vpos);
360
361 ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
362}
363
364/* HW access functions */
365
366void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
367{
368 uint8_t buf[] = {addr, val};
369 int ret;
370
371 ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
372 if (ret < 0)
373 ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
374 ret, addr);
375}
376
377uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
378{
379 uint8_t val;
380 int ret;
381
382 ret = i2c_master_send(client, &addr, sizeof(addr));
383 if (ret < 0)
384 goto fail;
385
386 ret = i2c_master_recv(client, &val, sizeof(val));
387 if (ret < 0)
388 goto fail;
389
390 return val;
391
392fail:
393 ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
394 ret, addr);
395 return 0;
396}
397
398void ch7006_state_load(struct i2c_client *client,
399 struct ch7006_state *state)
400{
401 ch7006_load_reg(client, state, CH7006_POWER);
402
403 ch7006_load_reg(client, state, CH7006_DISPMODE);
404 ch7006_load_reg(client, state, CH7006_FFILTER);
405 ch7006_load_reg(client, state, CH7006_BWIDTH);
406 ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
407 ch7006_load_reg(client, state, CH7006_CLKMODE);
408 ch7006_load_reg(client, state, CH7006_START_ACTIVE);
409 ch7006_load_reg(client, state, CH7006_POV);
410 ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
411 ch7006_load_reg(client, state, CH7006_HPOS);
412 ch7006_load_reg(client, state, CH7006_VPOS);
413 ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
414 ch7006_load_reg(client, state, CH7006_DETECT);
415 ch7006_load_reg(client, state, CH7006_CONTRAST);
416 ch7006_load_reg(client, state, CH7006_PLLOV);
417 ch7006_load_reg(client, state, CH7006_PLLM);
418 ch7006_load_reg(client, state, CH7006_PLLN);
419 ch7006_load_reg(client, state, CH7006_BCLKOUT);
420 ch7006_load_reg(client, state, CH7006_SUBC_INC0);
421 ch7006_load_reg(client, state, CH7006_SUBC_INC1);
422 ch7006_load_reg(client, state, CH7006_SUBC_INC2);
423 ch7006_load_reg(client, state, CH7006_SUBC_INC3);
424 ch7006_load_reg(client, state, CH7006_SUBC_INC4);
425 ch7006_load_reg(client, state, CH7006_SUBC_INC5);
426 ch7006_load_reg(client, state, CH7006_SUBC_INC6);
427 ch7006_load_reg(client, state, CH7006_SUBC_INC7);
428 ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
429 ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
430
431 /* I don't know what this is for, but otherwise I get no
432 * signal.
433 */
434 ch7006_write(client, 0x3d, 0x0);
435}
436
437void ch7006_state_save(struct i2c_client *client,
438 struct ch7006_state *state)
439{
440 ch7006_save_reg(client, state, CH7006_POWER);
441
442 ch7006_save_reg(client, state, CH7006_DISPMODE);
443 ch7006_save_reg(client, state, CH7006_FFILTER);
444 ch7006_save_reg(client, state, CH7006_BWIDTH);
445 ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
446 ch7006_save_reg(client, state, CH7006_CLKMODE);
447 ch7006_save_reg(client, state, CH7006_START_ACTIVE);
448 ch7006_save_reg(client, state, CH7006_POV);
449 ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
450 ch7006_save_reg(client, state, CH7006_HPOS);
451 ch7006_save_reg(client, state, CH7006_VPOS);
452 ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
453 ch7006_save_reg(client, state, CH7006_DETECT);
454 ch7006_save_reg(client, state, CH7006_CONTRAST);
455 ch7006_save_reg(client, state, CH7006_PLLOV);
456 ch7006_save_reg(client, state, CH7006_PLLM);
457 ch7006_save_reg(client, state, CH7006_PLLN);
458 ch7006_save_reg(client, state, CH7006_BCLKOUT);
459 ch7006_save_reg(client, state, CH7006_SUBC_INC0);
460 ch7006_save_reg(client, state, CH7006_SUBC_INC1);
461 ch7006_save_reg(client, state, CH7006_SUBC_INC2);
462 ch7006_save_reg(client, state, CH7006_SUBC_INC3);
463 ch7006_save_reg(client, state, CH7006_SUBC_INC4);
464 ch7006_save_reg(client, state, CH7006_SUBC_INC5);
465 ch7006_save_reg(client, state, CH7006_SUBC_INC6);
466 ch7006_save_reg(client, state, CH7006_SUBC_INC7);
467 ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
468 ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
469
470 state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
471 (state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
472 (state->regs[CH7006_FFILTER] & 0x03) << 2;
473}
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
new file mode 100644
index 000000000000..b06d3d93d8ac
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
@@ -0,0 +1,344 @@
1/*
2 * Copyright (C) 2009 Francisco Jerez.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#ifndef __DRM_I2C_CH7006_PRIV_H__
28#define __DRM_I2C_CH7006_PRIV_H__
29
30#include "drmP.h"
31#include "drm_crtc_helper.h"
32#include "drm_encoder_slave.h"
33#include "i2c/ch7006.h"
34
35typedef int64_t fixed;
36#define fixed1 (1LL << 32)
37
38enum ch7006_tv_norm {
39 TV_NORM_PAL,
40 TV_NORM_PAL_M,
41 TV_NORM_PAL_N,
42 TV_NORM_PAL_NC,
43 TV_NORM_PAL_60,
44 TV_NORM_NTSC_M,
45 TV_NORM_NTSC_J,
46 NUM_TV_NORMS
47};
48
49struct ch7006_tv_norm_info {
50 fixed vrefresh;
51 int vdisplay;
52 int vtotal;
53 int hvirtual;
54
55 fixed subc_freq;
56 fixed black_level;
57
58 uint32_t dispmode;
59 int voffset;
60};
61
62struct ch7006_mode {
63 struct drm_display_mode mode;
64
65 int enc_hdisp;
66 int enc_vdisp;
67
68 fixed subc_coeff;
69 uint32_t dispmode;
70
71 uint32_t valid_scales;
72 uint32_t valid_norms;
73};
74
75struct ch7006_state {
76 uint8_t regs[0x26];
77};
78
79struct ch7006_priv {
80 struct ch7006_encoder_params *params;
81 struct ch7006_mode *mode;
82
83 struct ch7006_state state;
84 struct ch7006_state saved_state;
85
86 struct drm_property *scale_property;
87
88 int select_subconnector;
89 int subconnector;
90 int hmargin;
91 int vmargin;
92 enum ch7006_tv_norm norm;
93 int brightness;
94 int contrast;
95 int flicker;
96 int scale;
97
98 int last_dpms;
99};
100
101#define to_ch7006_priv(x) \
102 ((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
103
104extern int ch7006_debug;
105extern char *ch7006_tv_norm;
106extern int ch7006_scale;
107
108extern char *ch7006_tv_norm_names[];
109extern struct ch7006_tv_norm_info ch7006_tv_norms[];
110extern struct ch7006_mode ch7006_modes[];
111
112struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
113 struct drm_display_mode *drm_mode);
114
115void ch7006_setup_levels(struct drm_encoder *encoder);
116void ch7006_setup_subcarrier(struct drm_encoder *encoder);
117void ch7006_setup_pll(struct drm_encoder *encoder);
118void ch7006_setup_power_state(struct drm_encoder *encoder);
119void ch7006_setup_properties(struct drm_encoder *encoder);
120
121void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
122uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
123
124void ch7006_state_load(struct i2c_client *client,
125 struct ch7006_state *state);
126void ch7006_state_save(struct i2c_client *client,
127 struct ch7006_state *state);
128
129/* Some helper macros */
130
131#define ch7006_dbg(client, format, ...) do { \
132 if (ch7006_debug) \
133 dev_printk(KERN_DEBUG, &client->dev, \
134 "%s: " format, __func__, ## __VA_ARGS__); \
135 } while (0)
136#define ch7006_info(client, format, ...) \
137 dev_info(&client->dev, format, __VA_ARGS__)
138#define ch7006_err(client, format, ...) \
139 dev_err(&client->dev, format, __VA_ARGS__)
140
141#define __mask(src, bitfield) \
142 (((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
143#define mask(bitfield) __mask(bitfield)
144
145#define __bitf(src, bitfield, x) \
146 (((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
147#define bitf(bitfield, x) __bitf(bitfield, x)
148#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
149#define setbitf(state, reg, bitfield, x) \
150 state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
151 | bitf(reg##_##bitfield, x)
152
153#define __unbitf(src, bitfield, x) \
154 ((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
155#define unbitf(bitfield, x) __unbitf(bitfield, x)
156
157static inline int interpolate(int y0, int y1, int y2, int x)
158{
159 return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
160}
161
162static inline int32_t round_fixed(fixed x)
163{
164 return (x + fixed1/2) >> 32;
165}
166
167#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
168#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
169
170/* Fixed hardware specs */
171
172#define CH7006_FREQ0 14318
173#define CH7006_MAXN 650
174#define CH7006_MAXM 315
175
176/* Register definitions */
177
178#define CH7006_DISPMODE 0x00
179#define CH7006_DISPMODE_INPUT_RES 0, 7:5
180#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
181#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
182#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
183#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
184#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
185#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
186#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
187#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
188#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
189#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
190#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
191#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
192#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
193#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
194#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
195#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
196#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
197#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
198
199#define CH7006_FFILTER 0x01
200#define CH7006_FFILTER_TEXT 0, 5:4
201#define CH7006_FFILTER_LUMA 0, 3:2
202#define CH7006_FFILTER_CHROMA 0, 1:0
203#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
204
205#define CH7006_BWIDTH 0x03
206#define CH7006_BWIDTH_5L_FFILER (1 << 7)
207#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
208#define CH7006_BWIDTH_CHROMA 0, 5:4
209#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
210#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
211#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
212
213#define CH7006_INPUT_FORMAT 0x04
214#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
215#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
216#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
217#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
218#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
219#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
220#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
221#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
222#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
223#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
224#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
225#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
226#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
227
228#define CH7006_CLKMODE 0x06
229#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
230#define CH7006_CLKMODE_MASTER (1 << 6)
231#define CH7006_CLKMODE_POS_EDGE (1 << 4)
232#define CH7006_CLKMODE_XCM 0, 3:2
233#define CH7006_CLKMODE_PCM 0, 1:0
234
235#define CH7006_START_ACTIVE 0x07
236#define CH7006_START_ACTIVE_0 0, 7:0
237
238#define CH7006_POV 0x08
239#define CH7006_POV_START_ACTIVE_8 8, 2:2
240#define CH7006_POV_HPOS_8 8, 1:1
241#define CH7006_POV_VPOS_8 8, 0:0
242
243#define CH7006_BLACK_LEVEL 0x09
244#define CH7006_BLACK_LEVEL_0 0, 7:0
245
246#define CH7006_HPOS 0x0a
247#define CH7006_HPOS_0 0, 7:0
248
249#define CH7006_VPOS 0x0b
250#define CH7006_VPOS_0 0, 7:0
251
252#define CH7006_INPUT_SYNC 0x0d
253#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
254#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
255#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
256#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
257
258#define CH7006_POWER 0x0e
259#define CH7006_POWER_SCART (1 << 4)
260#define CH7006_POWER_RESET (1 << 3)
261#define CH7006_POWER_LEVEL 0, 2:0
262#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
263#define CH7006_POWER_LEVEL_POWER_OFF 0x1
264#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
265#define CH7006_POWER_LEVEL_NORMAL 0x3
266#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
267
268#define CH7006_DETECT 0x10
269#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
270#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
271#define CH7006_DETECT_CVBS_TEST (1 << 1)
272#define CH7006_DETECT_SENSE (1 << 0)
273
274#define CH7006_CONTRAST 0x11
275#define CH7006_CONTRAST_0 0, 2:0
276
277#define CH7006_PLLOV 0x13
278#define CH7006_PLLOV_N_8 8, 2:1
279#define CH7006_PLLOV_M_8 8, 0:0
280
281#define CH7006_PLLM 0x14
282#define CH7006_PLLM_0 0, 7:0
283
284#define CH7006_PLLN 0x15
285#define CH7006_PLLN_0 0, 7:0
286
287#define CH7006_BCLKOUT 0x17
288
289#define CH7006_SUBC_INC0 0x18
290#define CH7006_SUBC_INC0_28 28, 3:0
291
292#define CH7006_SUBC_INC1 0x19
293#define CH7006_SUBC_INC1_24 24, 3:0
294
295#define CH7006_SUBC_INC2 0x1a
296#define CH7006_SUBC_INC2_20 20, 3:0
297
298#define CH7006_SUBC_INC3 0x1b
299#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
300#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
301#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
302#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
303#define CH7006_SUBC_INC3_16 16, 3:0
304
305#define CH7006_SUBC_INC4 0x1c
306#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
307#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
308#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
309#define CH7006_SUBC_INC4_12 12, 3:0
310
311#define CH7006_SUBC_INC5 0x1d
312#define CH7006_SUBC_INC5_8 8, 3:0
313
314#define CH7006_SUBC_INC6 0x1e
315#define CH7006_SUBC_INC6_4 4, 3:0
316
317#define CH7006_SUBC_INC7 0x1f
318#define CH7006_SUBC_INC7_0 0, 3:0
319
320#define CH7006_PLL_CONTROL 0x20
321#define CH7006_PLL_CONTROL_CPI (1 << 5)
322#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
323#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
324#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
325#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
326#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
327
328#define CH7006_CALC_SUBC_INC0 0x21
329#define CH7006_CALC_SUBC_INC0_24 24, 4:3
330#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
331#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
332
333#define CH7006_CALC_SUBC_INC1 0x22
334#define CH7006_CALC_SUBC_INC1_16 16, 7:0
335
336#define CH7006_CALC_SUBC_INC2 0x23
337#define CH7006_CALC_SUBC_INC2_8 8, 7:0
338
339#define CH7006_CALC_SUBC_INC3 0x24
340#define CH7006_CALC_SUBC_INC3_0 0, 7:0
341
342#define CH7006_VERSION_ID 0x25
343
344#endif