diff options
Diffstat (limited to 'drivers/gpu/drm/i2c')
-rw-r--r-- | drivers/gpu/drm/i2c/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/ch7006_drv.c | 536 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/ch7006_mode.c | 468 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/ch7006_priv.h | 344 |
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 @@ | |||
1 | ccflags-y := -Iinclude/drm | ||
2 | |||
3 | ch7006-y := ch7006_drv.o ch7006_mode.o | ||
4 | obj-$(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..81681a07a806 --- /dev/null +++ b/drivers/gpu/drm/i2c/ch7006_drv.c | |||
@@ -0,0 +1,536 @@ | |||
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 | |||
31 | static 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 | |||
39 | static 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 | |||
51 | static 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 | |||
68 | static 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 | |||
78 | static 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 | |||
88 | static 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 | |||
102 | static 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 | |||
111 | static 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 | |||
175 | static 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 | |||
223 | static 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 | |||
244 | static 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 | |||
280 | static 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 | |||
375 | static 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 | |||
393 | static 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 | /* I don't know what this is for, but otherwise I get no | ||
412 | * signal. | ||
413 | */ | ||
414 | ch7006_write(client, 0x3d, 0x0); | ||
415 | |||
416 | return 0; | ||
417 | |||
418 | fail: | ||
419 | ch7006_err(client, "Error %d reading version ID\n", ret); | ||
420 | |||
421 | return -ENODEV; | ||
422 | } | ||
423 | |||
424 | static int ch7006_remove(struct i2c_client *client) | ||
425 | { | ||
426 | ch7006_dbg(client, "\n"); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static int ch7006_encoder_init(struct i2c_client *client, | ||
432 | struct drm_device *dev, | ||
433 | struct drm_encoder_slave *encoder) | ||
434 | { | ||
435 | struct ch7006_priv *priv; | ||
436 | int i; | ||
437 | |||
438 | ch7006_dbg(client, "\n"); | ||
439 | |||
440 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
441 | if (!priv) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | encoder->slave_priv = priv; | ||
445 | encoder->slave_funcs = &ch7006_encoder_funcs; | ||
446 | |||
447 | priv->norm = TV_NORM_PAL; | ||
448 | priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; | ||
449 | priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; | ||
450 | priv->scale = 1; | ||
451 | priv->contrast = 50; | ||
452 | priv->brightness = 50; | ||
453 | priv->flicker = 50; | ||
454 | priv->hmargin = 50; | ||
455 | priv->vmargin = 50; | ||
456 | priv->last_dpms = -1; | ||
457 | |||
458 | if (ch7006_tv_norm) { | ||
459 | for (i = 0; i < NUM_TV_NORMS; i++) { | ||
460 | if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) { | ||
461 | priv->norm = i; | ||
462 | break; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | if (i == NUM_TV_NORMS) | ||
467 | ch7006_err(client, "Invalid TV norm setting \"%s\".\n", | ||
468 | ch7006_tv_norm); | ||
469 | } | ||
470 | |||
471 | if (ch7006_scale >= 0 && ch7006_scale <= 2) | ||
472 | priv->scale = ch7006_scale; | ||
473 | else | ||
474 | ch7006_err(client, "Invalid scale setting \"%d\".\n", | ||
475 | ch7006_scale); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static struct i2c_device_id ch7006_ids[] = { | ||
481 | { "ch7006", 0 }, | ||
482 | { } | ||
483 | }; | ||
484 | MODULE_DEVICE_TABLE(i2c, ch7006_ids); | ||
485 | |||
486 | static struct drm_i2c_encoder_driver ch7006_driver = { | ||
487 | .i2c_driver = { | ||
488 | .probe = ch7006_probe, | ||
489 | .remove = ch7006_remove, | ||
490 | |||
491 | .driver = { | ||
492 | .name = "ch7006", | ||
493 | }, | ||
494 | |||
495 | .id_table = ch7006_ids, | ||
496 | }, | ||
497 | |||
498 | .encoder_init = ch7006_encoder_init, | ||
499 | }; | ||
500 | |||
501 | |||
502 | /* Module initialization */ | ||
503 | |||
504 | static int __init ch7006_init(void) | ||
505 | { | ||
506 | return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver); | ||
507 | } | ||
508 | |||
509 | static void __exit ch7006_exit(void) | ||
510 | { | ||
511 | drm_i2c_encoder_unregister(&ch7006_driver); | ||
512 | } | ||
513 | |||
514 | int ch7006_debug; | ||
515 | module_param_named(debug, ch7006_debug, int, 0600); | ||
516 | MODULE_PARM_DESC(debug, "Enable debug output."); | ||
517 | |||
518 | char *ch7006_tv_norm; | ||
519 | module_param_named(tv_norm, ch7006_tv_norm, charp, 0600); | ||
520 | MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" | ||
521 | "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n" | ||
522 | "\t\tDefault: PAL"); | ||
523 | |||
524 | int ch7006_scale = 1; | ||
525 | module_param_named(scale, ch7006_scale, int, 0600); | ||
526 | MODULE_PARM_DESC(scale, "Default scale.\n" | ||
527 | "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n" | ||
528 | "\t\t\t1 -> Select default video modes.\n" | ||
529 | "\t\t\t2 -> Select video modes with a lower blanking ratio."); | ||
530 | |||
531 | MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>"); | ||
532 | MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver"); | ||
533 | MODULE_LICENSE("GPL and additional rights"); | ||
534 | |||
535 | module_init(ch7006_init); | ||
536 | module_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..e447dfb63890 --- /dev/null +++ b/drivers/gpu/drm/i2c/ch7006_mode.c | |||
@@ -0,0 +1,468 @@ | |||
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 | char *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 | |||
49 | struct 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 | |||
145 | struct 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 | |||
174 | struct 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 | |||
200 | void 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 | |||
231 | void 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 | |||
255 | void 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 | |||
293 | void 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 | |||
323 | void 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 | |||
366 | void 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 | |||
377 | uint8_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 | |||
392 | fail: | ||
393 | ch7006_err(client, "Error %d reading from subaddress 0x%x\n", | ||
394 | ret, addr); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | void 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 | |||
432 | void ch7006_state_save(struct i2c_client *client, | ||
433 | struct ch7006_state *state) | ||
434 | { | ||
435 | ch7006_save_reg(client, state, CH7006_POWER); | ||
436 | |||
437 | ch7006_save_reg(client, state, CH7006_DISPMODE); | ||
438 | ch7006_save_reg(client, state, CH7006_FFILTER); | ||
439 | ch7006_save_reg(client, state, CH7006_BWIDTH); | ||
440 | ch7006_save_reg(client, state, CH7006_INPUT_FORMAT); | ||
441 | ch7006_save_reg(client, state, CH7006_CLKMODE); | ||
442 | ch7006_save_reg(client, state, CH7006_START_ACTIVE); | ||
443 | ch7006_save_reg(client, state, CH7006_POV); | ||
444 | ch7006_save_reg(client, state, CH7006_BLACK_LEVEL); | ||
445 | ch7006_save_reg(client, state, CH7006_HPOS); | ||
446 | ch7006_save_reg(client, state, CH7006_VPOS); | ||
447 | ch7006_save_reg(client, state, CH7006_INPUT_SYNC); | ||
448 | ch7006_save_reg(client, state, CH7006_DETECT); | ||
449 | ch7006_save_reg(client, state, CH7006_CONTRAST); | ||
450 | ch7006_save_reg(client, state, CH7006_PLLOV); | ||
451 | ch7006_save_reg(client, state, CH7006_PLLM); | ||
452 | ch7006_save_reg(client, state, CH7006_PLLN); | ||
453 | ch7006_save_reg(client, state, CH7006_BCLKOUT); | ||
454 | ch7006_save_reg(client, state, CH7006_SUBC_INC0); | ||
455 | ch7006_save_reg(client, state, CH7006_SUBC_INC1); | ||
456 | ch7006_save_reg(client, state, CH7006_SUBC_INC2); | ||
457 | ch7006_save_reg(client, state, CH7006_SUBC_INC3); | ||
458 | ch7006_save_reg(client, state, CH7006_SUBC_INC4); | ||
459 | ch7006_save_reg(client, state, CH7006_SUBC_INC5); | ||
460 | ch7006_save_reg(client, state, CH7006_SUBC_INC6); | ||
461 | ch7006_save_reg(client, state, CH7006_SUBC_INC7); | ||
462 | ch7006_save_reg(client, state, CH7006_PLL_CONTROL); | ||
463 | ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0); | ||
464 | |||
465 | state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) | | ||
466 | (state->regs[CH7006_FFILTER] & 0x0c) >> 2 | | ||
467 | (state->regs[CH7006_FFILTER] & 0x03) << 2; | ||
468 | } | ||
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 | |||
35 | typedef int64_t fixed; | ||
36 | #define fixed1 (1LL << 32) | ||
37 | |||
38 | enum 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 | |||
49 | struct 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 | |||
62 | struct 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 | |||
75 | struct ch7006_state { | ||
76 | uint8_t regs[0x26]; | ||
77 | }; | ||
78 | |||
79 | struct 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 | |||
104 | extern int ch7006_debug; | ||
105 | extern char *ch7006_tv_norm; | ||
106 | extern int ch7006_scale; | ||
107 | |||
108 | extern char *ch7006_tv_norm_names[]; | ||
109 | extern struct ch7006_tv_norm_info ch7006_tv_norms[]; | ||
110 | extern struct ch7006_mode ch7006_modes[]; | ||
111 | |||
112 | struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder, | ||
113 | struct drm_display_mode *drm_mode); | ||
114 | |||
115 | void ch7006_setup_levels(struct drm_encoder *encoder); | ||
116 | void ch7006_setup_subcarrier(struct drm_encoder *encoder); | ||
117 | void ch7006_setup_pll(struct drm_encoder *encoder); | ||
118 | void ch7006_setup_power_state(struct drm_encoder *encoder); | ||
119 | void ch7006_setup_properties(struct drm_encoder *encoder); | ||
120 | |||
121 | void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val); | ||
122 | uint8_t ch7006_read(struct i2c_client *client, uint8_t addr); | ||
123 | |||
124 | void ch7006_state_load(struct i2c_client *client, | ||
125 | struct ch7006_state *state); | ||
126 | void 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 | |||
157 | static 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 | |||
162 | static 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 | ||