Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 1 | /* |
| 2 | * ths7303- THS7303 Video Amplifier driver |
| 3 | * |
| 4 | * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License as |
| 8 | * published by the Free Software Foundation version 2. |
| 9 | * |
| 10 | * This program is distributed .as is. WITHOUT ANY WARRANTY of any |
| 11 | * kind, whether express or implied; without even the implied warranty |
| 12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | */ |
| 15 | |
| 16 | #include <linux/kernel.h> |
| 17 | #include <linux/init.h> |
| 18 | #include <linux/ctype.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 19 | #include <linux/slab.h> |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 20 | #include <linux/i2c.h> |
| 21 | #include <linux/device.h> |
| 22 | #include <linux/delay.h> |
| 23 | #include <linux/module.h> |
| 24 | #include <linux/uaccess.h> |
| 25 | #include <linux/videodev2.h> |
| 26 | |
| 27 | #include <media/v4l2-device.h> |
| 28 | #include <media/v4l2-subdev.h> |
| 29 | #include <media/v4l2-chip-ident.h> |
| 30 | |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 31 | #define THS7303_CHANNEL_1 1 |
| 32 | #define THS7303_CHANNEL_2 2 |
| 33 | #define THS7303_CHANNEL_3 3 |
| 34 | |
| 35 | enum ths7303_filter_mode { |
| 36 | THS7303_FILTER_MODE_480I_576I, |
| 37 | THS7303_FILTER_MODE_480P_576P, |
| 38 | THS7303_FILTER_MODE_720P_1080I, |
| 39 | THS7303_FILTER_MODE_1080P, |
| 40 | THS7303_FILTER_MODE_DISABLE |
| 41 | }; |
| 42 | |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 43 | MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); |
| 44 | MODULE_AUTHOR("Chaithrika U S"); |
| 45 | MODULE_LICENSE("GPL"); |
| 46 | |
| 47 | static int debug; |
| 48 | module_param(debug, int, 0644); |
| 49 | MODULE_PARM_DESC(debug, "Debug level 0-1"); |
| 50 | |
| 51 | /* following function is used to set ths7303 */ |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 52 | int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 53 | { |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 54 | u8 input_bias_chroma = 3; |
| 55 | u8 input_bias_luma = 3; |
| 56 | int disable = 0; |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 57 | int err = 0; |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 58 | u8 val = 0; |
| 59 | u8 temp; |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 60 | |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 61 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 62 | |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 63 | if (!client) |
| 64 | return -EINVAL; |
| 65 | |
| 66 | switch (mode) { |
| 67 | case THS7303_FILTER_MODE_1080P: |
| 68 | val = (3 << 6); |
| 69 | val |= (3 << 3); |
| 70 | break; |
| 71 | case THS7303_FILTER_MODE_720P_1080I: |
| 72 | val = (2 << 6); |
| 73 | val |= (2 << 3); |
| 74 | break; |
| 75 | case THS7303_FILTER_MODE_480P_576P: |
| 76 | val = (1 << 6); |
| 77 | val |= (1 << 3); |
| 78 | break; |
| 79 | case THS7303_FILTER_MODE_480I_576I: |
| 80 | break; |
| 81 | case THS7303_FILTER_MODE_DISABLE: |
| 82 | pr_info("mode disabled\n"); |
| 83 | /* disable all channels */ |
| 84 | disable = 1; |
| 85 | default: |
| 86 | /* disable all channels */ |
| 87 | disable = 1; |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 88 | } |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 89 | /* Setup channel 2 - Luma - Green */ |
| 90 | temp = val; |
| 91 | if (!disable) |
| 92 | val |= input_bias_luma; |
| 93 | err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_2, val); |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 94 | if (err) |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 95 | goto out; |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 96 | |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 97 | /* setup two chroma channels */ |
| 98 | if (!disable) |
| 99 | temp |= input_bias_chroma; |
| 100 | |
| 101 | err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_1, temp); |
| 102 | if (err) |
| 103 | goto out; |
| 104 | |
| 105 | err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_3, temp); |
| 106 | if (err) |
| 107 | goto out; |
| 108 | return err; |
| 109 | out: |
| 110 | pr_info("write byte data failed\n"); |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 111 | return err; |
| 112 | } |
| 113 | |
| 114 | static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) |
| 115 | { |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 116 | if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) |
| 117 | return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I); |
| 118 | else |
| 119 | return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); |
| 120 | } |
| 121 | |
| 122 | /* for setting filter for HD output */ |
| 123 | static int ths7303_s_dv_timings(struct v4l2_subdev *sd, |
| 124 | struct v4l2_dv_timings *dv_timings) |
| 125 | { |
| 126 | u32 height = dv_timings->bt.height; |
| 127 | int interlaced = dv_timings->bt.interlaced; |
| 128 | int res = 0; |
| 129 | |
| 130 | if (height == 1080 && !interlaced) |
| 131 | res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P); |
| 132 | else if ((height == 720 && !interlaced) || |
| 133 | (height == 1080 && interlaced)) |
| 134 | res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I); |
| 135 | else if ((height == 480 || height == 576) && !interlaced) |
| 136 | res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P); |
| 137 | else |
| 138 | /* disable all channels */ |
| 139 | res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); |
| 140 | |
| 141 | return res; |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | static int ths7303_g_chip_ident(struct v4l2_subdev *sd, |
| 145 | struct v4l2_dbg_chip_ident *chip) |
| 146 | { |
| 147 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| 148 | |
| 149 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); |
| 150 | } |
| 151 | |
| 152 | static const struct v4l2_subdev_video_ops ths7303_video_ops = { |
| 153 | .s_std_output = ths7303_s_std_output, |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 154 | .s_dv_timings = ths7303_s_dv_timings, |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 155 | }; |
| 156 | |
| 157 | static const struct v4l2_subdev_core_ops ths7303_core_ops = { |
| 158 | .g_chip_ident = ths7303_g_chip_ident, |
| 159 | }; |
| 160 | |
| 161 | static const struct v4l2_subdev_ops ths7303_ops = { |
| 162 | .core = &ths7303_core_ops, |
| 163 | .video = &ths7303_video_ops, |
| 164 | }; |
| 165 | |
| 166 | static int ths7303_probe(struct i2c_client *client, |
| 167 | const struct i2c_device_id *id) |
| 168 | { |
| 169 | struct v4l2_subdev *sd; |
| 170 | v4l2_std_id std_id = V4L2_STD_NTSC; |
| 171 | |
| 172 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
| 173 | return -ENODEV; |
| 174 | |
| 175 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
| 176 | client->addr << 1, client->adapter->name); |
| 177 | |
| 178 | sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); |
| 179 | if (sd == NULL) |
| 180 | return -ENOMEM; |
| 181 | |
| 182 | v4l2_i2c_subdev_init(sd, client, &ths7303_ops); |
| 183 | |
Manjunath Hadli | ad7dcb3 | 2012-10-01 11:46:35 -0300 | [diff] [blame] | 184 | return ths7303_s_std_output(sd, std_id); |
Chaithrika U S | 40199c5 | 2009-05-07 09:29:25 -0300 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | static int ths7303_remove(struct i2c_client *client) |
| 188 | { |
| 189 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
| 190 | |
| 191 | v4l2_device_unregister_subdev(sd); |
| 192 | kfree(sd); |
| 193 | |
| 194 | return 0; |
| 195 | } |
| 196 | |
| 197 | static const struct i2c_device_id ths7303_id[] = { |
| 198 | {"ths7303", 0}, |
| 199 | {}, |
| 200 | }; |
| 201 | |
| 202 | MODULE_DEVICE_TABLE(i2c, ths7303_id); |
| 203 | |
| 204 | static struct i2c_driver ths7303_driver = { |
| 205 | .driver = { |
| 206 | .owner = THIS_MODULE, |
| 207 | .name = "ths7303", |
| 208 | }, |
| 209 | .probe = ths7303_probe, |
| 210 | .remove = ths7303_remove, |
| 211 | .id_table = ths7303_id, |
| 212 | }; |
| 213 | |
Axel Lin | c6e8d86 | 2012-02-12 06:56:32 -0300 | [diff] [blame] | 214 | module_i2c_driver(ths7303_driver); |