Merge remote-tracking branch 'regulator/topic/vctrl' into regulator-next
[sfrench/cifs-2.6.git] / drivers / media / i2c / vp27smpx.c
1 /*
2  * vp27smpx - driver version 0.0.1
3  *
4  * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
5  *
6  * Based on a tvaudio patch from Takahiro Adachi <tadachi@tadachi-net.com>
7  * and Kazuhiko Kawakami <kazz-0@mail.goo.ne.jp>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  */
19
20 #include <linux/module.h>
21 #include <linux/types.h>
22 #include <linux/slab.h>
23 #include <linux/ioctl.h>
24 #include <linux/uaccess.h>
25 #include <linux/i2c.h>
26 #include <linux/videodev2.h>
27 #include <media/v4l2-device.h>
28
29 MODULE_DESCRIPTION("vp27smpx driver");
30 MODULE_AUTHOR("Hans Verkuil");
31 MODULE_LICENSE("GPL");
32
33
34 /* ----------------------------------------------------------------------- */
35
36 struct vp27smpx_state {
37         struct v4l2_subdev sd;
38         int radio;
39         u32 audmode;
40 };
41
42 static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd)
43 {
44         return container_of(sd, struct vp27smpx_state, sd);
45 }
46
47 static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode)
48 {
49         struct vp27smpx_state *state = to_state(sd);
50         struct i2c_client *client = v4l2_get_subdevdata(sd);
51         u8 data[3] = { 0x00, 0x00, 0x04 };
52
53         switch (audmode) {
54         case V4L2_TUNER_MODE_MONO:
55         case V4L2_TUNER_MODE_LANG1:
56                 break;
57         case V4L2_TUNER_MODE_STEREO:
58         case V4L2_TUNER_MODE_LANG1_LANG2:
59                 data[1] = 0x01;
60                 break;
61         case V4L2_TUNER_MODE_LANG2:
62                 data[1] = 0x02;
63                 break;
64         }
65
66         if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
67                 v4l2_err(sd, "I/O error setting audmode\n");
68         else
69                 state->audmode = audmode;
70 }
71
72 static int vp27smpx_s_radio(struct v4l2_subdev *sd)
73 {
74         struct vp27smpx_state *state = to_state(sd);
75
76         state->radio = 1;
77         return 0;
78 }
79
80 static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
81 {
82         struct vp27smpx_state *state = to_state(sd);
83
84         state->radio = 0;
85         return 0;
86 }
87
88 static int vp27smpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
89 {
90         struct vp27smpx_state *state = to_state(sd);
91
92         if (!state->radio)
93                 vp27smpx_set_audmode(sd, vt->audmode);
94         return 0;
95 }
96
97 static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
98 {
99         struct vp27smpx_state *state = to_state(sd);
100
101         if (state->radio)
102                 return 0;
103         vt->audmode = state->audmode;
104         vt->capability = V4L2_TUNER_CAP_STEREO |
105                 V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
106         vt->rxsubchans = V4L2_TUNER_SUB_MONO;
107         return 0;
108 }
109
110 static int vp27smpx_log_status(struct v4l2_subdev *sd)
111 {
112         struct vp27smpx_state *state = to_state(sd);
113
114         v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode,
115                         state->radio ? " (Radio)" : "");
116         return 0;
117 }
118
119 /* ----------------------------------------------------------------------- */
120
121 static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
122         .log_status = vp27smpx_log_status,
123 };
124
125 static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
126         .s_radio = vp27smpx_s_radio,
127         .s_tuner = vp27smpx_s_tuner,
128         .g_tuner = vp27smpx_g_tuner,
129 };
130
131 static const struct v4l2_subdev_video_ops vp27smpx_video_ops = {
132         .s_std = vp27smpx_s_std,
133 };
134
135 static const struct v4l2_subdev_ops vp27smpx_ops = {
136         .core = &vp27smpx_core_ops,
137         .tuner = &vp27smpx_tuner_ops,
138         .video = &vp27smpx_video_ops,
139 };
140
141 /* ----------------------------------------------------------------------- */
142
143 /* i2c implementation */
144
145 /*
146  * Generic i2c probe
147  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
148  */
149
150 static int vp27smpx_probe(struct i2c_client *client,
151                           const struct i2c_device_id *id)
152 {
153         struct vp27smpx_state *state;
154         struct v4l2_subdev *sd;
155
156         /* Check if the adapter supports the needed features */
157         if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
158                 return -EIO;
159
160         v4l_info(client, "chip found @ 0x%x (%s)\n",
161                         client->addr << 1, client->adapter->name);
162
163         state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
164         if (state == NULL)
165                 return -ENOMEM;
166         sd = &state->sd;
167         v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops);
168         state->audmode = V4L2_TUNER_MODE_STEREO;
169
170         /* initialize vp27smpx */
171         vp27smpx_set_audmode(sd, state->audmode);
172         return 0;
173 }
174
175 static int vp27smpx_remove(struct i2c_client *client)
176 {
177         struct v4l2_subdev *sd = i2c_get_clientdata(client);
178
179         v4l2_device_unregister_subdev(sd);
180         return 0;
181 }
182
183 /* ----------------------------------------------------------------------- */
184
185 static const struct i2c_device_id vp27smpx_id[] = {
186         { "vp27smpx", 0 },
187         { }
188 };
189 MODULE_DEVICE_TABLE(i2c, vp27smpx_id);
190
191 static struct i2c_driver vp27smpx_driver = {
192         .driver = {
193                 .name   = "vp27smpx",
194         },
195         .probe          = vp27smpx_probe,
196         .remove         = vp27smpx_remove,
197         .id_table       = vp27smpx_id,
198 };
199
200 module_i2c_driver(vp27smpx_driver);