diff options
Diffstat (limited to 'drivers/gpu/drm/i2c/ch7006_drv.c')
-rw-r--r-- | drivers/gpu/drm/i2c/ch7006_drv.c | 531 |
1 files changed, 531 insertions, 0 deletions
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 | |||
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 | return 0; | ||
412 | |||
413 | fail: | ||
414 | ch7006_err(client, "Error %d reading version ID\n", ret); | ||
415 | |||
416 | return -ENODEV; | ||
417 | } | ||
418 | |||
419 | static int ch7006_remove(struct i2c_client *client) | ||
420 | { | ||
421 | ch7006_dbg(client, "\n"); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static 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 | |||
475 | static struct i2c_device_id ch7006_ids[] = { | ||
476 | { "ch7006", 0 }, | ||
477 | { } | ||
478 | }; | ||
479 | MODULE_DEVICE_TABLE(i2c, ch7006_ids); | ||
480 | |||
481 | static 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 | |||
499 | static int __init ch7006_init(void) | ||
500 | { | ||
501 | return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver); | ||
502 | } | ||
503 | |||
504 | static void __exit ch7006_exit(void) | ||
505 | { | ||
506 | drm_i2c_encoder_unregister(&ch7006_driver); | ||
507 | } | ||
508 | |||
509 | int ch7006_debug; | ||
510 | module_param_named(debug, ch7006_debug, int, 0600); | ||
511 | MODULE_PARM_DESC(debug, "Enable debug output."); | ||
512 | |||
513 | char *ch7006_tv_norm; | ||
514 | module_param_named(tv_norm, ch7006_tv_norm, charp, 0600); | ||
515 | MODULE_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 | |||
519 | int ch7006_scale = 1; | ||
520 | module_param_named(scale, ch7006_scale, int, 0600); | ||
521 | MODULE_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 | |||
526 | MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>"); | ||
527 | MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver"); | ||
528 | MODULE_LICENSE("GPL and additional rights"); | ||
529 | |||
530 | module_init(ch7006_init); | ||
531 | module_exit(ch7006_exit); | ||