• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Samsung HDMI Physical interface driver
3  *
4  * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
5  * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  */
12 
13 #include <linux/module.h>
14 #include <linux/i2c.h>
15 #include <linux/slab.h>
16 #include <linux/clk.h>
17 #include <linux/io.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/err.h>
21 
22 #include <media/v4l2-subdev.h>
23 
24 MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
25 MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
26 MODULE_LICENSE("GPL");
27 
28 struct hdmiphy_conf {
29 	unsigned long pixclk;
30 	const u8 *data;
31 };
32 
33 struct hdmiphy_ctx {
34 	struct v4l2_subdev sd;
35 	const struct hdmiphy_conf *conf_tab;
36 };
37 
38 static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = {
39 	{ .pixclk = 27000000, .data = (u8 [32]) {
40 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
41 		0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
42 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
43 		0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
44 	},
45 	{ .pixclk = 27027000, .data = (u8 [32]) {
46 		0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
47 		0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
48 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
49 		0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
50 	},
51 	{ .pixclk = 74176000, .data = (u8 [32]) {
52 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
53 		0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9,
54 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
55 		0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
56 	},
57 	{ .pixclk = 74250000, .data = (u8 [32]) {
58 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
59 		0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA,
60 		0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
61 		0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
62 	},
63 	{ /* end marker */ }
64 };
65 
66 static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = {
67 	{ .pixclk = 27000000, .data = (u8 [32]) {
68 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
69 		0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
70 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
71 		0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
72 	},
73 	{ .pixclk = 27027000, .data = (u8 [32]) {
74 		0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
75 		0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
76 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
77 		0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
78 	},
79 	{ .pixclk = 74176000, .data = (u8 [32]) {
80 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
81 		0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
82 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
83 		0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
84 	},
85 	{ .pixclk = 74250000, .data = (u8 [32]) {
86 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
87 		0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
88 		0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
89 		0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
90 	},
91 	{ .pixclk = 148352000, .data = (u8 [32]) {
92 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
93 		0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
94 		0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
95 		0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, }
96 	},
97 	{ .pixclk = 148500000, .data = (u8 [32]) {
98 		0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
99 		0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
100 		0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
101 		0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, }
102 	},
103 	{ /* end marker */ }
104 };
105 
106 static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = {
107 	{ .pixclk = 27000000, .data = (u8 [32]) {
108 		0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08,
109 		0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
110 		0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
111 		0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
112 	},
113 	{ .pixclk = 27027000, .data = (u8 [32]) {
114 		0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08,
115 		0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
116 		0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
117 		0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
118 	},
119 	{ .pixclk = 74176000, .data = (u8 [32]) {
120 		0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08,
121 		0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
122 		0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
123 		0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
124 	},
125 	{ .pixclk = 74250000, .data = (u8 [32]) {
126 		0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08,
127 		0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
128 		0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
129 		0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
130 	},
131 	{ .pixclk = 148500000, .data = (u8 [32]) {
132 		0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08,
133 		0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
134 		0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4,
135 		0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
136 	},
137 	{ /* end marker */ }
138 };
139 
140 static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = {
141 	{ .pixclk = 27000000, .data = (u8 [32]) {
142 		0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08,
143 		0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
144 		0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
145 		0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
146 	},
147 	{ .pixclk = 27027000, .data = (u8 [32]) {
148 		0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08,
149 		0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
150 		0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
151 		0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
152 	},
153 	{ .pixclk = 74176000, .data = (u8 [32]) {
154 		0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08,
155 		0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
156 		0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
157 		0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
158 	},
159 	{ .pixclk = 74250000, .data = (u8 [32]) {
160 		0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
161 		0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
162 		0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
163 		0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
164 	},
165 	{ .pixclk = 148500000, .data = (u8 [32]) {
166 		0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
167 		0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
168 		0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
169 		0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
170 	},
171 	{ /* end marker */ }
172 };
173 
sd_to_ctx(struct v4l2_subdev * sd)174 static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
175 {
176 	return container_of(sd, struct hdmiphy_ctx, sd);
177 }
178 
hdmiphy_find_conf(unsigned long pixclk,const struct hdmiphy_conf * conf)179 static const u8 *hdmiphy_find_conf(unsigned long pixclk,
180 		const struct hdmiphy_conf *conf)
181 {
182 	for (; conf->pixclk; ++conf)
183 		if (conf->pixclk == pixclk)
184 			return conf->data;
185 	return NULL;
186 }
187 
hdmiphy_s_power(struct v4l2_subdev * sd,int on)188 static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
189 {
190 	/* to be implemented */
191 	return 0;
192 }
193 
hdmiphy_s_dv_timings(struct v4l2_subdev * sd,struct v4l2_dv_timings * timings)194 static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,
195 	struct v4l2_dv_timings *timings)
196 {
197 	const u8 *data;
198 	u8 buffer[32];
199 	int ret;
200 	struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
201 	struct i2c_client *client = v4l2_get_subdevdata(sd);
202 	struct device *dev = &client->dev;
203 	unsigned long pixclk = timings->bt.pixelclock;
204 
205 	dev_info(dev, "s_dv_timings\n");
206 	if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000)
207 		pixclk = 74176000;
208 	data = hdmiphy_find_conf(pixclk, ctx->conf_tab);
209 	if (!data) {
210 		dev_err(dev, "format not supported\n");
211 		return -EINVAL;
212 	}
213 
214 	/* storing configuration to the device */
215 	memcpy(buffer, data, 32);
216 	ret = i2c_master_send(client, buffer, 32);
217 	if (ret != 32) {
218 		dev_err(dev, "failed to configure HDMIPHY via I2C\n");
219 		return -EIO;
220 	}
221 
222 	return 0;
223 }
224 
hdmiphy_dv_timings_cap(struct v4l2_subdev * sd,struct v4l2_dv_timings_cap * cap)225 static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
226 	struct v4l2_dv_timings_cap *cap)
227 {
228 	if (cap->pad != 0)
229 		return -EINVAL;
230 
231 	cap->type = V4L2_DV_BT_656_1120;
232 	/* The phy only determines the pixelclock, leave the other values
233 	 * at 0 to signify that we have no information for them. */
234 	cap->bt.min_pixelclock = 27000000;
235 	cap->bt.max_pixelclock = 148500000;
236 	return 0;
237 }
238 
hdmiphy_s_stream(struct v4l2_subdev * sd,int enable)239 static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
240 {
241 	struct i2c_client *client = v4l2_get_subdevdata(sd);
242 	struct device *dev = &client->dev;
243 	u8 buffer[2];
244 	int ret;
245 
246 	dev_info(dev, "s_stream(%d)\n", enable);
247 	/* going to/from configuration from/to operation mode */
248 	buffer[0] = 0x1f;
249 	buffer[1] = enable ? 0x80 : 0x00;
250 
251 	ret = i2c_master_send(client, buffer, 2);
252 	if (ret != 2) {
253 		dev_err(dev, "stream (%d) failed\n", enable);
254 		return -EIO;
255 	}
256 	return 0;
257 }
258 
259 static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
260 	.s_power =  hdmiphy_s_power,
261 };
262 
263 static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
264 	.s_dv_timings = hdmiphy_s_dv_timings,
265 	.s_stream =  hdmiphy_s_stream,
266 };
267 
268 static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
269 	.dv_timings_cap = hdmiphy_dv_timings_cap,
270 };
271 
272 static const struct v4l2_subdev_ops hdmiphy_ops = {
273 	.core = &hdmiphy_core_ops,
274 	.video = &hdmiphy_video_ops,
275 	.pad = &hdmiphy_pad_ops,
276 };
277 
hdmiphy_probe(struct i2c_client * client,const struct i2c_device_id * id)278 static int hdmiphy_probe(struct i2c_client *client,
279 			 const struct i2c_device_id *id)
280 {
281 	struct hdmiphy_ctx *ctx;
282 
283 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
284 	if (!ctx)
285 		return -ENOMEM;
286 
287 	ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
288 	v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
289 
290 	dev_info(&client->dev, "probe successful\n");
291 	return 0;
292 }
293 
hdmiphy_remove(struct i2c_client * client)294 static int hdmiphy_remove(struct i2c_client *client)
295 {
296 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
297 	struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
298 
299 	kfree(ctx);
300 	dev_info(&client->dev, "remove successful\n");
301 
302 	return 0;
303 }
304 
305 static const struct i2c_device_id hdmiphy_id[] = {
306 	{ "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
307 	{ "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
308 	{ "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
309 	{ "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
310 	{ "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
311 	{ },
312 };
313 MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
314 
315 static struct i2c_driver hdmiphy_driver = {
316 	.driver = {
317 		.name	= "s5p-hdmiphy",
318 		.owner	= THIS_MODULE,
319 	},
320 	.probe		= hdmiphy_probe,
321 	.remove		= hdmiphy_remove,
322 	.id_table = hdmiphy_id,
323 };
324 
325 module_i2c_driver(hdmiphy_driver);
326