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	u32 preset;
30	const u8 *data;
31};
32
33static const u8 hdmiphy_conf27[32] = {
34	0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
35	0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87,
36	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
37	0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00,
38};
39
40static const u8 hdmiphy_conf74_175[32] = {
41	0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
42	0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
43	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
44	0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
45};
46
47static const u8 hdmiphy_conf74_25[32] = {
48	0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
49	0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
50	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0,
51	0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
52};
53
54static const u8 hdmiphy_conf148_5[32] = {
55	0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
56	0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
57	0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
58	0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
59};
60
61static const struct hdmiphy_conf hdmiphy_conf[] = {
62	{ V4L2_DV_480P59_94, hdmiphy_conf27 },
63	{ V4L2_DV_1080P30, hdmiphy_conf74_175 },
64	{ V4L2_DV_720P59_94, hdmiphy_conf74_175 },
65	{ V4L2_DV_720P60, hdmiphy_conf74_25 },
66	{ V4L2_DV_1080P50, hdmiphy_conf148_5 },
67	{ V4L2_DV_1080P60, hdmiphy_conf148_5 },
68};
69
70const u8 *hdmiphy_preset2conf(u32 preset)
71{
72	int i;
73	for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i)
74		if (hdmiphy_conf[i].preset == preset)
75			return hdmiphy_conf[i].data;
76	return NULL;
77}
78
79static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
80{
81	/* to be implemented */
82	return 0;
83}
84
85static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
86	struct v4l2_dv_preset *preset)
87{
88	const u8 *data;
89	u8 buffer[32];
90	int ret;
91	struct i2c_client *client = v4l2_get_subdevdata(sd);
92	struct device *dev = &client->dev;
93
94	dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
95	data = hdmiphy_preset2conf(preset->preset);
96	if (!data) {
97		dev_err(dev, "format not supported\n");
98		return -EINVAL;
99	}
100
101	/* storing configuration to the device */
102	memcpy(buffer, data, 32);
103	ret = i2c_master_send(client, buffer, 32);
104	if (ret != 32) {
105		dev_err(dev, "failed to configure HDMIPHY via I2C\n");
106		return -EIO;
107	}
108
109	return 0;
110}
111
112static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
113{
114	struct i2c_client *client = v4l2_get_subdevdata(sd);
115	struct device *dev = &client->dev;
116	u8 buffer[2];
117	int ret;
118
119	dev_info(dev, "s_stream(%d)\n", enable);
120	/* going to/from configuration from/to operation mode */
121	buffer[0] = 0x1f;
122	buffer[1] = enable ? 0x80 : 0x00;
123
124	ret = i2c_master_send(client, buffer, 2);
125	if (ret != 2) {
126		dev_err(dev, "stream (%d) failed\n", enable);
127		return -EIO;
128	}
129	return 0;
130}
131
132static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
133	.s_power =  hdmiphy_s_power,
134};
135
136static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
137	.s_dv_preset = hdmiphy_s_dv_preset,
138	.s_stream =  hdmiphy_s_stream,
139};
140
141static const struct v4l2_subdev_ops hdmiphy_ops = {
142	.core = &hdmiphy_core_ops,
143	.video = &hdmiphy_video_ops,
144};
145
146static int __devinit hdmiphy_probe(struct i2c_client *client,
147	const struct i2c_device_id *id)
148{
149	static struct v4l2_subdev sd;
150
151	v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops);
152	dev_info(&client->dev, "probe successful\n");
153	return 0;
154}
155
156static int __devexit hdmiphy_remove(struct i2c_client *client)
157{
158	dev_info(&client->dev, "remove successful\n");
159	return 0;
160}
161
162static const struct i2c_device_id hdmiphy_id[] = {
163	{ "hdmiphy", 0 },
164	{ },
165};
166MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
167
168static struct i2c_driver hdmiphy_driver = {
169	.driver = {
170		.name	= "s5p-hdmiphy",
171		.owner	= THIS_MODULE,
172	},
173	.probe		= hdmiphy_probe,
174	.remove		= __devexit_p(hdmiphy_remove),
175	.id_table = hdmiphy_id,
176};
177
178module_i2c_driver(hdmiphy_driver);
179