xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/i2c/ch7006_drv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2009 Francisco Jerez.
3*4882a593Smuzhiyun  * All Rights Reserved.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Permission is hereby granted, free of charge, to any person obtaining
6*4882a593Smuzhiyun  * a copy of this software and associated documentation files (the
7*4882a593Smuzhiyun  * "Software"), to deal in the Software without restriction, including
8*4882a593Smuzhiyun  * without limitation the rights to use, copy, modify, merge, publish,
9*4882a593Smuzhiyun  * distribute, sublicense, and/or sell copies of the Software, and to
10*4882a593Smuzhiyun  * permit persons to whom the Software is furnished to do so, subject to
11*4882a593Smuzhiyun  * the following conditions:
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * The above copyright notice and this permission notice (including the
14*4882a593Smuzhiyun  * next paragraph) shall be included in all copies or substantial
15*4882a593Smuzhiyun  * portions of the Software.
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18*4882a593Smuzhiyun  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19*4882a593Smuzhiyun  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20*4882a593Smuzhiyun  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21*4882a593Smuzhiyun  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22*4882a593Smuzhiyun  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23*4882a593Smuzhiyun  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <linux/module.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include "ch7006_priv.h"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun /* DRM encoder functions */
32*4882a593Smuzhiyun 
ch7006_encoder_set_config(struct drm_encoder * encoder,void * params)33*4882a593Smuzhiyun static void ch7006_encoder_set_config(struct drm_encoder *encoder,
34*4882a593Smuzhiyun 				      void *params)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	priv->params = *(struct ch7006_encoder_params *)params;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun 
ch7006_encoder_destroy(struct drm_encoder * encoder)41*4882a593Smuzhiyun static void ch7006_encoder_destroy(struct drm_encoder *encoder)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	drm_property_destroy(encoder->dev, priv->scale_property);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	kfree(priv);
48*4882a593Smuzhiyun 	to_encoder_slave(encoder)->slave_priv = NULL;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	drm_i2c_encoder_destroy(encoder);
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
ch7006_encoder_dpms(struct drm_encoder * encoder,int mode)53*4882a593Smuzhiyun static void  ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
56*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
57*4882a593Smuzhiyun 	struct ch7006_state *state = &priv->state;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	if (mode == priv->last_dpms)
62*4882a593Smuzhiyun 		return;
63*4882a593Smuzhiyun 	priv->last_dpms = mode;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	ch7006_setup_power_state(encoder);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	ch7006_load_reg(client, state, CH7006_POWER);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
ch7006_encoder_save(struct drm_encoder * encoder)70*4882a593Smuzhiyun static void ch7006_encoder_save(struct drm_encoder *encoder)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
73*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	ch7006_state_save(client, &priv->saved_state);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
ch7006_encoder_restore(struct drm_encoder * encoder)80*4882a593Smuzhiyun static void ch7006_encoder_restore(struct drm_encoder *encoder)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
83*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	ch7006_state_load(client, &priv->saved_state);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun 
ch7006_encoder_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)90*4882a593Smuzhiyun static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
91*4882a593Smuzhiyun 				      const struct drm_display_mode *mode,
92*4882a593Smuzhiyun 				      struct drm_display_mode *adjusted_mode)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	/* The ch7006 is painfully picky with the input timings so no
97*4882a593Smuzhiyun 	 * custom modes for now... */
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	priv->mode = ch7006_lookup_mode(encoder, mode);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	return !!priv->mode;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
ch7006_encoder_mode_valid(struct drm_encoder * encoder,struct drm_display_mode * mode)104*4882a593Smuzhiyun static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
105*4882a593Smuzhiyun 				     struct drm_display_mode *mode)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	if (ch7006_lookup_mode(encoder, mode))
108*4882a593Smuzhiyun 		return MODE_OK;
109*4882a593Smuzhiyun 	else
110*4882a593Smuzhiyun 		return MODE_BAD;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
ch7006_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * drm_mode,struct drm_display_mode * adjusted_mode)113*4882a593Smuzhiyun static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
114*4882a593Smuzhiyun 				     struct drm_display_mode *drm_mode,
115*4882a593Smuzhiyun 				     struct drm_display_mode *adjusted_mode)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
118*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
119*4882a593Smuzhiyun 	struct ch7006_encoder_params *params = &priv->params;
120*4882a593Smuzhiyun 	struct ch7006_state *state = &priv->state;
121*4882a593Smuzhiyun 	uint8_t *regs = state->regs;
122*4882a593Smuzhiyun 	const struct ch7006_mode *mode = priv->mode;
123*4882a593Smuzhiyun 	const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
124*4882a593Smuzhiyun 	int start_active;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
129*4882a593Smuzhiyun 	regs[CH7006_BWIDTH] = 0;
130*4882a593Smuzhiyun 	regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
131*4882a593Smuzhiyun 					 params->input_format);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
134*4882a593Smuzhiyun 		| bitf(CH7006_CLKMODE_XCM, params->xcm)
135*4882a593Smuzhiyun 		| bitf(CH7006_CLKMODE_PCM, params->pcm);
136*4882a593Smuzhiyun 	if (params->clock_mode)
137*4882a593Smuzhiyun 		regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
138*4882a593Smuzhiyun 	if (params->clock_edge)
139*4882a593Smuzhiyun 		regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
142*4882a593Smuzhiyun 	regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
143*4882a593Smuzhiyun 	regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	regs[CH7006_INPUT_SYNC] = 0;
146*4882a593Smuzhiyun 	if (params->sync_direction)
147*4882a593Smuzhiyun 		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
148*4882a593Smuzhiyun 	if (params->sync_encoding)
149*4882a593Smuzhiyun 		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
150*4882a593Smuzhiyun 	if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
151*4882a593Smuzhiyun 		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
152*4882a593Smuzhiyun 	if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
153*4882a593Smuzhiyun 		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	regs[CH7006_DETECT] = 0;
156*4882a593Smuzhiyun 	regs[CH7006_BCLKOUT] = 0;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	regs[CH7006_SUBC_INC3] = 0;
159*4882a593Smuzhiyun 	if (params->pout_level)
160*4882a593Smuzhiyun 		regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	regs[CH7006_SUBC_INC4] = 0;
163*4882a593Smuzhiyun 	if (params->active_detect)
164*4882a593Smuzhiyun 		regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	ch7006_setup_levels(encoder);
169*4882a593Smuzhiyun 	ch7006_setup_subcarrier(encoder);
170*4882a593Smuzhiyun 	ch7006_setup_pll(encoder);
171*4882a593Smuzhiyun 	ch7006_setup_power_state(encoder);
172*4882a593Smuzhiyun 	ch7006_setup_properties(encoder);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	ch7006_state_load(client, state);
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
ch7006_encoder_detect(struct drm_encoder * encoder,struct drm_connector * connector)177*4882a593Smuzhiyun static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
178*4882a593Smuzhiyun 						       struct drm_connector *connector)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
181*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
182*4882a593Smuzhiyun 	struct ch7006_state *state = &priv->state;
183*4882a593Smuzhiyun 	int det;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	ch7006_save_reg(client, state, CH7006_DETECT);
188*4882a593Smuzhiyun 	ch7006_save_reg(client, state, CH7006_POWER);
189*4882a593Smuzhiyun 	ch7006_save_reg(client, state, CH7006_CLKMODE);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
192*4882a593Smuzhiyun 					   bitfs(CH7006_POWER_LEVEL, NORMAL));
193*4882a593Smuzhiyun 	ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	ch7006_write(client, CH7006_DETECT, 0);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	det = ch7006_read(client, CH7006_DETECT);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	ch7006_load_reg(client, state, CH7006_CLKMODE);
202*4882a593Smuzhiyun 	ch7006_load_reg(client, state, CH7006_POWER);
203*4882a593Smuzhiyun 	ch7006_load_reg(client, state, CH7006_DETECT);
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
206*4882a593Smuzhiyun 		    CH7006_DETECT_SVIDEO_C_TEST|
207*4882a593Smuzhiyun 		    CH7006_DETECT_CVBS_TEST)) == 0)
208*4882a593Smuzhiyun 		priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
209*4882a593Smuzhiyun 	else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
210*4882a593Smuzhiyun 			 CH7006_DETECT_SVIDEO_C_TEST)) == 0)
211*4882a593Smuzhiyun 		priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
212*4882a593Smuzhiyun 	else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
213*4882a593Smuzhiyun 		priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
214*4882a593Smuzhiyun 	else
215*4882a593Smuzhiyun 		priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	drm_object_property_set_value(&connector->base,
218*4882a593Smuzhiyun 			encoder->dev->mode_config.tv_subconnector_property,
219*4882a593Smuzhiyun 							priv->subconnector);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	return priv->subconnector ? connector_status_connected :
222*4882a593Smuzhiyun 					connector_status_disconnected;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun 
ch7006_encoder_get_modes(struct drm_encoder * encoder,struct drm_connector * connector)225*4882a593Smuzhiyun static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
226*4882a593Smuzhiyun 				    struct drm_connector *connector)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
229*4882a593Smuzhiyun 	const struct ch7006_mode *mode;
230*4882a593Smuzhiyun 	int n = 0;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	for (mode = ch7006_modes; mode->mode.clock; mode++) {
233*4882a593Smuzhiyun 		if (~mode->valid_scales & 1<<priv->scale ||
234*4882a593Smuzhiyun 		    ~mode->valid_norms & 1<<priv->norm)
235*4882a593Smuzhiyun 			continue;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		drm_mode_probed_add(connector,
238*4882a593Smuzhiyun 				drm_mode_duplicate(encoder->dev, &mode->mode));
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 		n++;
241*4882a593Smuzhiyun 	}
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	return n;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
ch7006_encoder_create_resources(struct drm_encoder * encoder,struct drm_connector * connector)246*4882a593Smuzhiyun static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
247*4882a593Smuzhiyun 					   struct drm_connector *connector)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
250*4882a593Smuzhiyun 	struct drm_device *dev = encoder->dev;
251*4882a593Smuzhiyun 	struct drm_mode_config *conf = &dev->mode_config;
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2);
256*4882a593Smuzhiyun 	if (!priv->scale_property)
257*4882a593Smuzhiyun 		return -ENOMEM;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_select_subconnector_property,
260*4882a593Smuzhiyun 				      priv->select_subconnector);
261*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_subconnector_property,
262*4882a593Smuzhiyun 				      priv->subconnector);
263*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_left_margin_property,
264*4882a593Smuzhiyun 				      priv->hmargin);
265*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_bottom_margin_property,
266*4882a593Smuzhiyun 				      priv->vmargin);
267*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_mode_property,
268*4882a593Smuzhiyun 				      priv->norm);
269*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_brightness_property,
270*4882a593Smuzhiyun 				      priv->brightness);
271*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_contrast_property,
272*4882a593Smuzhiyun 				      priv->contrast);
273*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, conf->tv_flicker_reduction_property,
274*4882a593Smuzhiyun 				      priv->flicker);
275*4882a593Smuzhiyun 	drm_object_attach_property(&connector->base, priv->scale_property,
276*4882a593Smuzhiyun 				      priv->scale);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	return 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
ch7006_encoder_set_property(struct drm_encoder * encoder,struct drm_connector * connector,struct drm_property * property,uint64_t val)281*4882a593Smuzhiyun static int ch7006_encoder_set_property(struct drm_encoder *encoder,
282*4882a593Smuzhiyun 				       struct drm_connector *connector,
283*4882a593Smuzhiyun 				       struct drm_property *property,
284*4882a593Smuzhiyun 				       uint64_t val)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
287*4882a593Smuzhiyun 	struct ch7006_priv *priv = to_ch7006_priv(encoder);
288*4882a593Smuzhiyun 	struct ch7006_state *state = &priv->state;
289*4882a593Smuzhiyun 	struct drm_mode_config *conf = &encoder->dev->mode_config;
290*4882a593Smuzhiyun 	struct drm_crtc *crtc = encoder->crtc;
291*4882a593Smuzhiyun 	bool modes_changed = false;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	if (property == conf->tv_select_subconnector_property) {
296*4882a593Smuzhiyun 		priv->select_subconnector = val;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 		ch7006_setup_power_state(encoder);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_POWER);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	} else if (property == conf->tv_left_margin_property) {
303*4882a593Smuzhiyun 		priv->hmargin = val;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 		ch7006_setup_properties(encoder);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_POV);
308*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_HPOS);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	} else if (property == conf->tv_bottom_margin_property) {
311*4882a593Smuzhiyun 		priv->vmargin = val;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 		ch7006_setup_properties(encoder);
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_POV);
316*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_VPOS);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	} else if (property == conf->tv_mode_property) {
319*4882a593Smuzhiyun 		if (connector->dpms != DRM_MODE_DPMS_OFF)
320*4882a593Smuzhiyun 			return -EINVAL;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 		priv->norm = val;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 		modes_changed = true;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	} else if (property == conf->tv_brightness_property) {
327*4882a593Smuzhiyun 		priv->brightness = val;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 		ch7006_setup_levels(encoder);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	} else if (property == conf->tv_contrast_property) {
334*4882a593Smuzhiyun 		priv->contrast = val;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 		ch7006_setup_properties(encoder);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_CONTRAST);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	} else if (property == conf->tv_flicker_reduction_property) {
341*4882a593Smuzhiyun 		priv->flicker = val;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 		ch7006_setup_properties(encoder);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 		ch7006_load_reg(client, state, CH7006_FFILTER);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	} else if (property == priv->scale_property) {
348*4882a593Smuzhiyun 		if (connector->dpms != DRM_MODE_DPMS_OFF)
349*4882a593Smuzhiyun 			return -EINVAL;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 		priv->scale = val;
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 		modes_changed = true;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	} else {
356*4882a593Smuzhiyun 		return -EINVAL;
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (modes_changed) {
360*4882a593Smuzhiyun 		drm_helper_probe_single_connector_modes(connector, 0, 0);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 		if (crtc)
363*4882a593Smuzhiyun 			drm_crtc_helper_set_mode(crtc, &crtc->mode,
364*4882a593Smuzhiyun 						 crtc->x, crtc->y,
365*4882a593Smuzhiyun 						 crtc->primary->fb);
366*4882a593Smuzhiyun 	}
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	return 0;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun static const struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
372*4882a593Smuzhiyun 	.set_config = ch7006_encoder_set_config,
373*4882a593Smuzhiyun 	.destroy = ch7006_encoder_destroy,
374*4882a593Smuzhiyun 	.dpms = ch7006_encoder_dpms,
375*4882a593Smuzhiyun 	.save = ch7006_encoder_save,
376*4882a593Smuzhiyun 	.restore = ch7006_encoder_restore,
377*4882a593Smuzhiyun 	.mode_fixup = ch7006_encoder_mode_fixup,
378*4882a593Smuzhiyun 	.mode_valid = ch7006_encoder_mode_valid,
379*4882a593Smuzhiyun 	.mode_set = ch7006_encoder_mode_set,
380*4882a593Smuzhiyun 	.detect = ch7006_encoder_detect,
381*4882a593Smuzhiyun 	.get_modes = ch7006_encoder_get_modes,
382*4882a593Smuzhiyun 	.create_resources = ch7006_encoder_create_resources,
383*4882a593Smuzhiyun 	.set_property = ch7006_encoder_set_property,
384*4882a593Smuzhiyun };
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun /* I2C driver functions */
388*4882a593Smuzhiyun 
ch7006_probe(struct i2c_client * client,const struct i2c_device_id * id)389*4882a593Smuzhiyun static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	uint8_t addr = CH7006_VERSION_ID;
392*4882a593Smuzhiyun 	uint8_t val;
393*4882a593Smuzhiyun 	int ret;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	ret = i2c_master_send(client, &addr, sizeof(addr));
398*4882a593Smuzhiyun 	if (ret < 0)
399*4882a593Smuzhiyun 		goto fail;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	ret = i2c_master_recv(client, &val, sizeof(val));
402*4882a593Smuzhiyun 	if (ret < 0)
403*4882a593Smuzhiyun 		goto fail;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	ch7006_info(client, "Detected version ID: %x\n", val);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	/* I don't know what this is for, but otherwise I get no
408*4882a593Smuzhiyun 	 * signal.
409*4882a593Smuzhiyun 	 */
410*4882a593Smuzhiyun 	ch7006_write(client, 0x3d, 0x0);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	return 0;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun fail:
415*4882a593Smuzhiyun 	ch7006_err(client, "Error %d reading version ID\n", ret);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	return -ENODEV;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun 
ch7006_remove(struct i2c_client * client)420*4882a593Smuzhiyun static int ch7006_remove(struct i2c_client *client)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	return 0;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun 
ch7006_resume(struct device * dev)427*4882a593Smuzhiyun static int ch7006_resume(struct device *dev)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	struct i2c_client *client = to_i2c_client(dev);
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	ch7006_write(client, 0x3d, 0x0);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	return 0;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun 
ch7006_encoder_init(struct i2c_client * client,struct drm_device * dev,struct drm_encoder_slave * encoder)438*4882a593Smuzhiyun static int ch7006_encoder_init(struct i2c_client *client,
439*4882a593Smuzhiyun 			       struct drm_device *dev,
440*4882a593Smuzhiyun 			       struct drm_encoder_slave *encoder)
441*4882a593Smuzhiyun {
442*4882a593Smuzhiyun 	struct ch7006_priv *priv;
443*4882a593Smuzhiyun 	int i;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	ch7006_dbg(client, "\n");
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
448*4882a593Smuzhiyun 	if (!priv)
449*4882a593Smuzhiyun 		return -ENOMEM;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	encoder->slave_priv = priv;
452*4882a593Smuzhiyun 	encoder->slave_funcs = &ch7006_encoder_funcs;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	priv->norm = TV_NORM_PAL;
455*4882a593Smuzhiyun 	priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
456*4882a593Smuzhiyun 	priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
457*4882a593Smuzhiyun 	priv->scale = 1;
458*4882a593Smuzhiyun 	priv->contrast = 50;
459*4882a593Smuzhiyun 	priv->brightness = 50;
460*4882a593Smuzhiyun 	priv->flicker = 50;
461*4882a593Smuzhiyun 	priv->hmargin = 50;
462*4882a593Smuzhiyun 	priv->vmargin = 50;
463*4882a593Smuzhiyun 	priv->last_dpms = -1;
464*4882a593Smuzhiyun 	priv->chip_version = ch7006_read(client, CH7006_VERSION_ID);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	if (ch7006_tv_norm) {
467*4882a593Smuzhiyun 		for (i = 0; i < NUM_TV_NORMS; i++) {
468*4882a593Smuzhiyun 			if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
469*4882a593Smuzhiyun 				priv->norm = i;
470*4882a593Smuzhiyun 				break;
471*4882a593Smuzhiyun 			}
472*4882a593Smuzhiyun 		}
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 		if (i == NUM_TV_NORMS)
475*4882a593Smuzhiyun 			ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
476*4882a593Smuzhiyun 				   ch7006_tv_norm);
477*4882a593Smuzhiyun 	}
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	if (ch7006_scale >= 0 && ch7006_scale <= 2)
480*4882a593Smuzhiyun 		priv->scale = ch7006_scale;
481*4882a593Smuzhiyun 	else
482*4882a593Smuzhiyun 		ch7006_err(client, "Invalid scale setting \"%d\".\n",
483*4882a593Smuzhiyun 			   ch7006_scale);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	return 0;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun static const struct i2c_device_id ch7006_ids[] = {
489*4882a593Smuzhiyun 	{ "ch7006", 0 },
490*4882a593Smuzhiyun 	{ }
491*4882a593Smuzhiyun };
492*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, ch7006_ids);
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun static const struct dev_pm_ops ch7006_pm_ops = {
495*4882a593Smuzhiyun 	.resume = ch7006_resume,
496*4882a593Smuzhiyun };
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun static struct drm_i2c_encoder_driver ch7006_driver = {
499*4882a593Smuzhiyun 	.i2c_driver = {
500*4882a593Smuzhiyun 		.probe = ch7006_probe,
501*4882a593Smuzhiyun 		.remove = ch7006_remove,
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 		.driver = {
504*4882a593Smuzhiyun 			.name = "ch7006",
505*4882a593Smuzhiyun 			.pm = &ch7006_pm_ops,
506*4882a593Smuzhiyun 		},
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 		.id_table = ch7006_ids,
509*4882a593Smuzhiyun 	},
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	.encoder_init = ch7006_encoder_init,
512*4882a593Smuzhiyun };
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun /* Module initialization */
516*4882a593Smuzhiyun 
ch7006_init(void)517*4882a593Smuzhiyun static int __init ch7006_init(void)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun 	return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
ch7006_exit(void)522*4882a593Smuzhiyun static void __exit ch7006_exit(void)
523*4882a593Smuzhiyun {
524*4882a593Smuzhiyun 	drm_i2c_encoder_unregister(&ch7006_driver);
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun int ch7006_debug;
528*4882a593Smuzhiyun module_param_named(debug, ch7006_debug, int, 0600);
529*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Enable debug output.");
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun char *ch7006_tv_norm;
532*4882a593Smuzhiyun module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
533*4882a593Smuzhiyun MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
534*4882a593Smuzhiyun 		 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
535*4882a593Smuzhiyun 		 "\t\tDefault: PAL");
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun int ch7006_scale = 1;
538*4882a593Smuzhiyun module_param_named(scale, ch7006_scale, int, 0600);
539*4882a593Smuzhiyun MODULE_PARM_DESC(scale, "Default scale.\n"
540*4882a593Smuzhiyun 		 "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
541*4882a593Smuzhiyun 		 "\t\t\t1 -> Select default video modes.\n"
542*4882a593Smuzhiyun 		 "\t\t\t2 -> Select video modes with a lower blanking ratio.");
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
545*4882a593Smuzhiyun MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
546*4882a593Smuzhiyun MODULE_LICENSE("GPL and additional rights");
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun module_init(ch7006_init);
549*4882a593Smuzhiyun module_exit(ch7006_exit);
550