2 * Copyright (C) 2009 Francisco Jerez.
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:
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.
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.
27 #include "ch7006_priv.h"
29 /* DRM encoder functions */
31 static void ch7006_encoder_set_config(struct drm_encoder
*encoder
,
34 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
36 priv
->params
= params
;
39 static void ch7006_encoder_destroy(struct drm_encoder
*encoder
)
41 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
43 drm_property_destroy(encoder
->dev
, priv
->scale_property
);
46 to_encoder_slave(encoder
)->slave_priv
= NULL
;
48 drm_i2c_encoder_destroy(encoder
);
51 static void ch7006_encoder_dpms(struct drm_encoder
*encoder
, int mode
)
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
;
57 ch7006_dbg(client
, "\n");
59 if (mode
== priv
->last_dpms
)
61 priv
->last_dpms
= mode
;
63 ch7006_setup_power_state(encoder
);
65 ch7006_load_reg(client
, state
, CH7006_POWER
);
68 static void ch7006_encoder_save(struct drm_encoder
*encoder
)
70 struct i2c_client
*client
= drm_i2c_encoder_get_client(encoder
);
71 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
73 ch7006_dbg(client
, "\n");
75 ch7006_state_save(client
, &priv
->saved_state
);
78 static void ch7006_encoder_restore(struct drm_encoder
*encoder
)
80 struct i2c_client
*client
= drm_i2c_encoder_get_client(encoder
);
81 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
83 ch7006_dbg(client
, "\n");
85 ch7006_state_load(client
, &priv
->saved_state
);
88 static bool ch7006_encoder_mode_fixup(struct drm_encoder
*encoder
,
89 struct drm_display_mode
*mode
,
90 struct drm_display_mode
*adjusted_mode
)
92 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
94 /* The ch7006 is painfully picky with the input timings so no
95 * custom modes for now... */
97 priv
->mode
= ch7006_lookup_mode(encoder
, mode
);
102 static int ch7006_encoder_mode_valid(struct drm_encoder
*encoder
,
103 struct drm_display_mode
*mode
)
105 if (ch7006_lookup_mode(encoder
, mode
))
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
)
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
];
124 ch7006_dbg(client
, "\n");
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
);
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
;
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
);
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
;
153 regs
[CH7006_DETECT
] = 0;
154 regs
[CH7006_BCLKOUT
] = 0;
156 regs
[CH7006_SUBC_INC3
] = 0;
157 if (params
->pout_level
)
158 regs
[CH7006_SUBC_INC3
] |= CH7006_SUBC_INC3_POUT_3_3V
;
160 regs
[CH7006_SUBC_INC4
] = 0;
161 if (params
->active_detect
)
162 regs
[CH7006_SUBC_INC4
] |= CH7006_SUBC_INC4_DS_INPUT
;
164 regs
[CH7006_PLL_CONTROL
] = priv
->saved_state
.regs
[CH7006_PLL_CONTROL
];
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
);
172 ch7006_state_load(client
, state
);
175 static enum drm_connector_status
ch7006_encoder_detect(struct drm_encoder
*encoder
,
176 struct drm_connector
*connector
)
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
;
183 ch7006_dbg(client
, "\n");
185 ch7006_save_reg(client
, state
, CH7006_DETECT
);
186 ch7006_save_reg(client
, state
, CH7006_POWER
);
187 ch7006_save_reg(client
, state
, CH7006_CLKMODE
);
189 ch7006_write(client
, CH7006_POWER
, CH7006_POWER_RESET
|
190 bitfs(CH7006_POWER_LEVEL
, NORMAL
));
191 ch7006_write(client
, CH7006_CLKMODE
, CH7006_CLKMODE_MASTER
);
193 ch7006_write(client
, CH7006_DETECT
, CH7006_DETECT_SENSE
);
195 ch7006_write(client
, CH7006_DETECT
, 0);
197 det
= ch7006_read(client
, CH7006_DETECT
);
199 ch7006_load_reg(client
, state
, CH7006_CLKMODE
);
200 ch7006_load_reg(client
, state
, CH7006_POWER
);
201 ch7006_load_reg(client
, state
, CH7006_DETECT
);
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
;
213 priv
->subconnector
= DRM_MODE_SUBCONNECTOR_Unknown
;
215 drm_connector_property_set_value(connector
,
216 encoder
->dev
->mode_config
.tv_subconnector_property
,
219 return priv
->subconnector
? connector_status_connected
:
220 connector_status_disconnected
;
223 static int ch7006_encoder_get_modes(struct drm_encoder
*encoder
,
224 struct drm_connector
*connector
)
226 struct ch7006_priv
*priv
= to_ch7006_priv(encoder
);
227 struct ch7006_mode
*mode
;
230 for (mode
= ch7006_modes
; mode
->mode
.clock
; mode
++) {
231 if (~mode
->valid_scales
& 1<<priv
->scale
||
232 ~mode
->valid_norms
& 1<<priv
->norm
)
235 drm_mode_probed_add(connector
,
236 drm_mode_duplicate(encoder
->dev
, &mode
->mode
));
244 static int ch7006_encoder_create_resources(struct drm_encoder
*encoder
,
245 struct drm_connector
*connector
)
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
;
251 drm_mode_create_tv_properties(dev
, NUM_TV_NORMS
, ch7006_tv_norm_names
);
253 priv
->scale_property
= drm_property_create(dev
, DRM_MODE_PROP_RANGE
,
255 priv
->scale_property
->values
[0] = 0;
256 priv
->scale_property
->values
[1] = 2;
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
,
262 drm_connector_attach_property(connector
, conf
->tv_left_margin_property
,
264 drm_connector_attach_property(connector
, conf
->tv_bottom_margin_property
,
266 drm_connector_attach_property(connector
, conf
->tv_mode_property
,
268 drm_connector_attach_property(connector
, conf
->tv_brightness_property
,
270 drm_connector_attach_property(connector
, conf
->tv_contrast_property
,
272 drm_connector_attach_property(connector
, conf
->tv_flicker_reduction_property
,
274 drm_connector_attach_property(connector
, priv
->scale_property
,
280 static int ch7006_encoder_set_property(struct drm_encoder
*encoder
,
281 struct drm_connector
*connector
,
282 struct drm_property
*property
,
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;
292 ch7006_dbg(client
, "\n");
294 if (property
== conf
->tv_select_subconnector_property
) {
295 priv
->select_subconnector
= val
;
297 ch7006_setup_power_state(encoder
);
299 ch7006_load_reg(client
, state
, CH7006_POWER
);
301 } else if (property
== conf
->tv_left_margin_property
) {
304 ch7006_setup_properties(encoder
);
306 ch7006_load_reg(client
, state
, CH7006_POV
);
307 ch7006_load_reg(client
, state
, CH7006_HPOS
);
309 } else if (property
== conf
->tv_bottom_margin_property
) {
312 ch7006_setup_properties(encoder
);
314 ch7006_load_reg(client
, state
, CH7006_POV
);
315 ch7006_load_reg(client
, state
, CH7006_VPOS
);
317 } else if (property
== conf
->tv_mode_property
) {
318 if (connector
->dpms
!= DRM_MODE_DPMS_OFF
)
323 modes_changed
= true;
325 } else if (property
== conf
->tv_brightness_property
) {
326 priv
->brightness
= val
;
328 ch7006_setup_levels(encoder
);
330 ch7006_load_reg(client
, state
, CH7006_BLACK_LEVEL
);
332 } else if (property
== conf
->tv_contrast_property
) {
333 priv
->contrast
= val
;
335 ch7006_setup_properties(encoder
);
337 ch7006_load_reg(client
, state
, CH7006_CONTRAST
);
339 } else if (property
== conf
->tv_flicker_reduction_property
) {
342 ch7006_setup_properties(encoder
);
344 ch7006_load_reg(client
, state
, CH7006_FFILTER
);
346 } else if (property
== priv
->scale_property
) {
347 if (connector
->dpms
!= DRM_MODE_DPMS_OFF
)
352 modes_changed
= true;
359 drm_helper_probe_single_connector_modes(connector
, 0, 0);
361 /* Disable the crtc to ensure a full modeset is
362 * performed whenever it's turned on again. */
364 struct drm_mode_set modeset
= {
368 crtc
->funcs
->set_config(&modeset
);
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
,
391 /* I2C driver functions */
393 static int ch7006_probe(struct i2c_client
*client
, const struct i2c_device_id
*id
)
395 uint8_t addr
= CH7006_VERSION_ID
;
399 ch7006_dbg(client
, "\n");
401 ret
= i2c_master_send(client
, &addr
, sizeof(addr
));
405 ret
= i2c_master_recv(client
, &val
, sizeof(val
));
409 ch7006_info(client
, "Detected version ID: %x\n", val
);
411 /* I don't know what this is for, but otherwise I get no
414 ch7006_write(client
, 0x3d, 0x0);
419 ch7006_err(client
, "Error %d reading version ID\n", ret
);
424 static int ch7006_remove(struct i2c_client
*client
)
426 ch7006_dbg(client
, "\n");
431 static int ch7006_encoder_init(struct i2c_client
*client
,
432 struct drm_device
*dev
,
433 struct drm_encoder_slave
*encoder
)
435 struct ch7006_priv
*priv
;
438 ch7006_dbg(client
, "\n");
440 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
444 encoder
->slave_priv
= priv
;
445 encoder
->slave_funcs
= &ch7006_encoder_funcs
;
447 priv
->norm
= TV_NORM_PAL
;
448 priv
->select_subconnector
= DRM_MODE_SUBCONNECTOR_Automatic
;
449 priv
->subconnector
= DRM_MODE_SUBCONNECTOR_Unknown
;
452 priv
->brightness
= 50;
456 priv
->last_dpms
= -1;
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
)) {
466 if (i
== NUM_TV_NORMS
)
467 ch7006_err(client
, "Invalid TV norm setting \"%s\".\n",
471 if (ch7006_scale
>= 0 && ch7006_scale
<= 2)
472 priv
->scale
= ch7006_scale
;
474 ch7006_err(client
, "Invalid scale setting \"%d\".\n",
480 static struct i2c_device_id ch7006_ids
[] = {
484 MODULE_DEVICE_TABLE(i2c
, ch7006_ids
);
486 static struct drm_i2c_encoder_driver ch7006_driver
= {
488 .probe
= ch7006_probe
,
489 .remove
= ch7006_remove
,
495 .id_table
= ch7006_ids
,
498 .encoder_init
= ch7006_encoder_init
,
502 /* Module initialization */
504 static int __init
ch7006_init(void)
506 return drm_i2c_encoder_register(THIS_MODULE
, &ch7006_driver
);
509 static void __exit
ch7006_exit(void)
511 drm_i2c_encoder_unregister(&ch7006_driver
);
515 module_param_named(debug
, ch7006_debug
, int, 0600);
516 MODULE_PARM_DESC(debug
, "Enable debug output.");
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"
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.");
531 MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
532 MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
533 MODULE_LICENSE("GPL and additional rights");
535 module_init(ch7006_init
);
536 module_exit(ch7006_exit
);