[go: nahoru, domu]

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
24MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
25MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
26MODULE_LICENSE("GPL");
27
28struct hdmiphy_conf {
29	unsigned long pixclk;
30	const u8 *data;
31};
32
33struct hdmiphy_ctx {
34	struct v4l2_subdev sd;
35	const struct hdmiphy_conf *conf_tab;
36};
37
38static 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
66static 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
106static 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
140static 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
174static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
175{
176	return container_of(sd, struct hdmiphy_ctx, sd);
177}
178
179static 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
188static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
189{
190	/* to be implemented */
191	return 0;
192}
193
194static 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
225static 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
239static 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
259static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
260	.s_power =  hdmiphy_s_power,
261};
262
263static 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
268static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
269	.dv_timings_cap = hdmiphy_dv_timings_cap,
270};
271
272static const struct v4l2_subdev_ops hdmiphy_ops = {
273	.core = &hdmiphy_core_ops,
274	.video = &hdmiphy_video_ops,
275	.pad = &hdmiphy_pad_ops,
276};
277
278static 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
294static 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
305static 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};
313MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
314
315static 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
325module_i2c_driver(hdmiphy_driver);
326