1 /*
2 ** =============================================================================
3 ** Copyright (c) 2016 Texas Instruments Inc.
4 **
5 ** This program is free software; you can redistribute it and/or modify it under
6 ** the terms of the GNU General Public License as published by the Free Software
7 ** Foundation; version 2.
8 **
9 ** This program is distributed in the hope that it will be useful, but WITHOUT
10 ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 **
13 ** You should have received a copy of the GNU General Public License along with
14 ** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 ** Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 **
17 ** File:
18 ** tiload.c
19 **
20 ** Description:
21 ** utility for TAS2555 Android in-system tuning
22 **
23 ** =============================================================================
24 */
27 #include <linux/module.h>
28 #include <linux/moduleparam.h>
29 #include <linux/init.h>
30 #include <linux/kernel.h>
31 #include <linux/fs.h>
32 #include <linux/types.h>
33 #include <linux/kdev_t.h>
34 #include <linux/cdev.h>
35 #include <linux/device.h>
36 #include <asm/io.h>
37 #include <linux/slab.h>
38 #include <linux/delay.h>
39 #include <linux/i2c.h>
40 #include <linux/platform_device.h>
41 #include <asm/uaccess.h>
43 #include "tiload.h"
45 /* enable debug prints in the driver */
46 #define DEBUG
48 static struct cdev *tiload_cdev;
49 static int tiload_major = 0; /* Dynamic allocation of Mjr No. */
50 static int tiload_opened = 0; /* Dynamic allocation of Mjr No. */
51 static struct tas2555_priv *g_TAS2555;
52 struct class *tiload_class;
53 static unsigned int magic_num = 0x00;
55 static char gPage = 0;
56 static char gBook = 0;
57 /******************************** Debug section *****************************/
59 /*
60 *----------------------------------------------------------------------------
61 * Function : tiload_open
62 *
63 * Purpose : open method for tiload programming interface
64 *----------------------------------------------------------------------------
65 */
66 static int tiload_open(struct inode *in, struct file *filp)
67 {
68 struct tas2555_priv *pTAS2555 = g_TAS2555;
70 dev_info(pTAS2555->dev, "%s\n", __FUNCTION__);
72 if (tiload_opened) {
73 dev_info(pTAS2555->dev, "%s device is already opened\n", "tiload");
74 return -1;
75 }
76 filp->private_data = (void*)pTAS2555;
77 tiload_opened++;
78 return 0;
79 }
81 /*
82 *----------------------------------------------------------------------------
83 * Function : tiload_release
84 *
85 * Purpose : close method for tiload programming interface
86 *----------------------------------------------------------------------------
87 */
88 static int tiload_release(struct inode *in, struct file *filp)
89 {
90 struct tas2555_priv *pTAS2555 = (struct tas2555_priv *)filp->private_data;
92 dev_info(pTAS2555->dev, "%s\n", __FUNCTION__);
93 filp->private_data = NULL;
94 tiload_opened--;
95 return 0;
96 }
98 #define MAX_LENGTH 128
99 /*
100 *----------------------------------------------------------------------------
101 * Function : tiload_read
102 *
103 * Purpose : read from codec
104 *----------------------------------------------------------------------------
105 */
106 static ssize_t tiload_read(struct file *filp, char __user * buf,
107 size_t count, loff_t * offset)
108 {
109 struct tas2555_priv *pTAS2555 = (struct tas2555_priv *)filp->private_data;
110 static char rd_data[MAX_LENGTH + 1];
111 unsigned int nCompositeRegister = 0, Value = 0;
112 //unsigned int n;
113 char reg_addr;
114 size_t size;
115 int ret = 0;
116 #ifdef DEBUG
117 //int i;
118 #endif
119 // struct i2c_client *i2c = g_codec->control_data;
121 dev_dbg(pTAS2555->dev, "%s\n", __FUNCTION__);
122 if (count > MAX_LENGTH) {
123 dev_err(pTAS2555->dev, "Max %d bytes can be read\n", MAX_LENGTH);
124 return -1;
125 }
127 /* copy register address from user space */
128 size = copy_from_user(®_addr, buf, 1);
129 if (size != 0) {
130 dev_err(pTAS2555->dev, "read: copy_from_user failure\n");
131 return -1;
132 }
134 size = count;
136 nCompositeRegister = BPR_REG(gBook, gPage, reg_addr);
137 if (count == 1) {
138 ret = pTAS2555->read(pTAS2555,
139 pTAS2555->mnCurrentChannel,
140 0x80000000 | nCompositeRegister, &Value);
141 if (ret >= 0) rd_data[0] = (char) Value;
142 } else if (count > 1) {
143 ret = pTAS2555->bulk_read(pTAS2555,
144 pTAS2555->mnCurrentChannel,
145 0x80000000 | nCompositeRegister, rd_data, size);
146 }
147 if (ret < 0)
148 dev_err(pTAS2555->dev,
149 "%s, %d, ret=%d, count=%zu error happen!\n",
150 __FUNCTION__, __LINE__, ret, count);
152 #ifdef DEBUG
153 dev_dbg(pTAS2555->dev,
154 "read size = %d, reg_addr= 0x%x , count = %d\n",
155 (int) size, reg_addr, (int) count);
156 // for (i = 0; i < (int) size; i++) {
157 // dev_dbg(pTAS2557->dev, "rd_data[%d]=%x\n", i, rd_data[i]);
158 // }
159 #endif
160 if (size != count) {
161 dev_err(pTAS2555->dev,
162 "read %d registers from the codec\n", (int) size);
163 }
165 if (copy_to_user(buf, rd_data, size) != 0) {
166 dev_err(pTAS2555->dev, "copy_to_user failed\n");
167 return -1;
168 }
170 return size;
171 }
173 /*
174 *----------------------------------------------------------------------------
175 * Function : tiload_write
176 *
177 * Purpose : write to codec
178 *----------------------------------------------------------------------------
179 */
180 static ssize_t tiload_write(struct file *filp, const char __user * buf,
181 size_t count, loff_t * offset)
182 {
183 struct tas2555_priv *pTAS2555 = (struct tas2555_priv *)filp->private_data;
184 static char wr_data[MAX_LENGTH + 1];
185 char *pData = wr_data;
186 size_t size;
187 unsigned int nCompositeRegister = 0;
188 // u8 pg_no;
189 // unsigned int n;
190 unsigned int nRegister;
191 int ret = 0;
192 #ifdef DEBUG
193 //int i;
194 #endif
196 dev_info(pTAS2555->dev, "%s\n", __FUNCTION__);
198 if (count > MAX_LENGTH) {
199 dev_err(pTAS2555->dev,"Max %d bytes can be read\n", MAX_LENGTH);
200 return -1;
201 }
203 /* copy buffer from user space */
204 size = copy_from_user(wr_data, buf, count);
205 if (size != 0) {
206 dev_err(pTAS2555->dev,
207 "copy_from_user failure %d\n", (int) size);
208 return -1;
209 }
210 #ifdef DEBUG
211 dev_dbg(pTAS2555->dev, "write size = %zu\n", count);
212 //for (i = 0; i < (int) count; i++) {
213 // dev_info(pTAS2557->dev, "wr_data[%d]=%x\n", i, wr_data[i]);
214 //}
215 #endif
216 nRegister = wr_data[0];
217 size = count;
218 if ((nRegister == 127) && (gPage == 0)) {
219 gBook = wr_data[1];
220 return size;
221 }
223 if (nRegister == 0) {
224 gPage = wr_data[1];
225 pData++;
226 count--;
227 }
228 #if 1
229 nCompositeRegister = BPR_REG(gBook, gPage, nRegister);
230 if (count == 2) {
231 ret = pTAS2555->write(pTAS2555,
232 pTAS2555->mnCurrentChannel,
233 0x80000000 | nCompositeRegister, pData[1]);
234 } else if (count > 2) {
235 ret = pTAS2555->bulk_write(pTAS2555,
236 pTAS2555->mnCurrentChannel,
237 0x80000000 | nCompositeRegister, &pData[1], count - 1);
238 }
239 if (ret < 0)
240 dev_err(pTAS2555->dev,
241 "%s, %d, ret=%d, count=%zu, ERROR Happen\n",
242 __FUNCTION__, __LINE__, ret, count);
243 #else
244 for (n = 1; n < count; n++) {
245 nCompositeRegister = BPR_REG(gBook, gPage, nRegister + n - 1);
246 g_codec->driver->write(g_codec, 0x80000000 | nCompositeRegister,
247 pData[n]);
248 }
249 #endif
251 return size;
252 }
254 static void tiload_route_IO(struct tas2555_priv *pTAS2555,
255 unsigned int bLock)
256 {
257 if (bLock) {
258 pTAS2555->write(pTAS2555,
259 pTAS2555->mnCurrentChannel,
260 0xAFFEAFFE, 0xBABEBABE);
261 } else {
262 pTAS2555->write(pTAS2555,
263 pTAS2555->mnCurrentChannel,
264 0xBABEBABE, 0xAFFEAFFE);
265 }
266 }
268 static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
269 {
270 long num = 0;
271 struct tas2555_priv *pTAS2555 = (struct tas2555_priv *)filp->private_data;
272 void __user *argp = (void __user *) arg;
273 unsigned char addr = 0;
274 int val;
275 BPR bpr;
277 dev_info(pTAS2555->dev, "%s, cmd=0x%x\n", __FUNCTION__, cmd);
278 // if (_IOC_TYPE(cmd) != TILOAD_IOC_MAGIC)
279 // return -ENOTTY;
281 switch (cmd) {
282 case TILOAD_IOMAGICNUM_GET:
283 num = copy_to_user(argp, &magic_num, sizeof(int));
284 break;
285 case TILOAD_IOMAGICNUM_SET:
286 num = copy_from_user(&magic_num, argp, sizeof(int));
287 dev_info(pTAS2555->dev, "TILOAD_IOMAGICNUM_SET\n");
288 tiload_route_IO(pTAS2555, magic_num);
289 break;
290 case TILOAD_BPR_READ:
291 break;
292 case TILOAD_BPR_WRITE:
293 num = copy_from_user(&bpr, argp, sizeof(BPR));
294 dev_dbg(pTAS2555->dev,
295 "TILOAD_BPR_WRITE: 0x%02X, 0x%02X, 0x%02X\n\r", bpr.nBook,
296 bpr.nPage, bpr.nRegister);
297 break;
298 case TILOAD_IOCTL_SET_CHL:
299 num = copy_from_user(&val, argp, sizeof(int));
300 addr = (unsigned char)(val>>1);
301 if(addr == pTAS2555->mnLAddr){
302 dev_info(pTAS2555->dev, "TILOAD_IOCTL_SET_CHL left\n");
303 pTAS2555->mnCurrentChannel = channel_left;
304 }else if(addr == pTAS2555->mnRAddr){
305 dev_info(pTAS2555->dev, "TILOAD_IOCTL_SET_CHL right\n");
306 pTAS2555->mnCurrentChannel = channel_right;
307 }else{
308 dev_err(pTAS2555->dev, "TILOAD_IOCTL_SET_CHL error L(0x%x) R(0x%x) 0x%x,\n",
309 pTAS2555->mnLAddr, pTAS2555->mnRAddr, addr);
310 }
311 break;
312 case TILOAD_IOCTL_SET_CONFIG:
313 num = copy_from_user(&val, argp, sizeof(val));
314 pTAS2555->set_config(pTAS2555, val);
315 break;
316 case TILOAD_IOCTL_SET_CALIBRATION:
317 num = copy_from_user(&val, argp, sizeof(val));
318 pTAS2555->set_calibration(pTAS2555, val);
319 break;
320 default:
321 break;
322 }
323 return num;
324 }
326 /*********** File operations structure for tiload *************/
327 static struct file_operations tiload_fops = {
328 .owner = THIS_MODULE,
329 .open = tiload_open,
330 .release = tiload_release,
331 .read = tiload_read,
332 .write = tiload_write,
333 .unlocked_ioctl = tiload_ioctl,
334 };
336 /*
337 *----------------------------------------------------------------------------
338 * Function : tiload_driver_init
339 *
340 * Purpose : Register a char driver for dynamic tiload programming
341 *----------------------------------------------------------------------------
342 */
343 int tiload_driver_init(struct tas2555_priv *pTAS2555)
344 {
345 int result;
347 dev_t dev = MKDEV(tiload_major, 0);
349 dev_info(pTAS2555->dev, "%s\n", __FUNCTION__);
350 g_TAS2555 = pTAS2555;
352 result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
353 if (result < 0) {
354 dev_err(pTAS2555->dev,
355 "cannot allocate major number %d\n", tiload_major);
356 return result;
357 }
358 tiload_class = class_create(THIS_MODULE, DEVICE_NAME);
359 tiload_major = MAJOR(dev);
360 dev_info(pTAS2555->dev,
361 "allocated Major Number: %d\n", tiload_major);
363 tiload_cdev = cdev_alloc();
364 cdev_init(tiload_cdev, &tiload_fops);
365 tiload_cdev->owner = THIS_MODULE;
366 tiload_cdev->ops = &tiload_fops;
368 if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL)
369 dev_err(pTAS2555->dev,
370 "Device creation failed\n");
372 if (cdev_add(tiload_cdev, dev, 1) < 0) {
373 dev_err(pTAS2555->dev,
374 "tiload_driver: cdev_add failed \n");
375 unregister_chrdev_region(dev, 1);
376 tiload_cdev = NULL;
377 return 1;
378 }
380 dev_info(pTAS2555->dev,
381 "Registered TiLoad driver, Major number: %d \n", tiload_major);
383 return 0;
384 }
386 MODULE_AUTHOR("Texas Instruments Inc.");
387 MODULE_DESCRIPTION("Utility for TAS2555 Android in-system tuning");
388 MODULE_LICENSE("GPLv2");
389 //#endif