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 ** File:
14 ** tiload.c
15 **
16 ** Description:
17 ** utility for TAS2557 Android in-system tuning
18 **
19 ** =============================================================================
20 */
23 #include <linux/module.h>
24 #include <linux/moduleparam.h>
25 #include <linux/init.h>
26 #include <linux/kernel.h>
27 #include <linux/fs.h>
28 #include <linux/types.h>
29 #include <linux/kdev_t.h>
30 #include <linux/cdev.h>
31 #include <linux/device.h>
32 #include <linux/io.h>
33 #include <linux/delay.h>
34 #include <linux/i2c.h>
35 #include <linux/platform_device.h>
36 #include <linux/uaccess.h>
38 #include "tiload.h"
40 /* enable debug prints in the driver */
41 #define DEBUG
43 static struct cdev *tiload_cdev;
44 static int tiload_major; /* Dynamic allocation of Mjr No. */
45 static int tiload_opened; /* Dynamic allocation of Mjr No. */
46 static struct tas2557_priv *g_TAS2557;
47 struct class *tiload_class;
48 static unsigned int magic_num;
50 static char gPage;
51 static char gBook;
52 /******************************** Debug section *****************************/
55 /*----------------------------------------------------------------------------
56 * Function : tiload_open
57 *
58 * Purpose : open method for tiload programming interface
59 *----------------------------------------------------------------------------
60 */
61 static int tiload_open(struct inode *in, struct file *filp)
62 {
63 struct tas2557_priv *pTAS2557 = g_TAS2557;
65 dev_info(pTAS2557->dev, "%s\n", __func__);
67 if (tiload_opened) {
68 dev_info(pTAS2557->dev, "%s device is already opened\n", "tiload");
69 return -EINVAL;
70 }
71 filp->private_data = (void *)pTAS2557;
72 tiload_opened++;
73 return 0;
74 }
76 /*----------------------------------------------------------------------------
77 * Function : tiload_release
78 *
79 * Purpose : close method for tiload programming interface
80 *----------------------------------------------------------------------------
81 */
82 static int tiload_release(struct inode *in, struct file *filp)
83 {
84 struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
86 dev_info(pTAS2557->dev, "%s\n", __func__);
87 filp->private_data = NULL;
88 tiload_opened--;
89 return 0;
90 }
92 #define MAX_LENGTH 128
93 /*----------------------------------------------------------------------------
94 * Function : tiload_read
95 *
96 * Purpose : read from codec
97 *----------------------------------------------------------------------------
98 */
99 static ssize_t tiload_read(struct file *filp, char __user *buf,
100 size_t count, loff_t *offset)
101 {
102 struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
103 static char rd_data[MAX_LENGTH + 1];
104 unsigned int nCompositeRegister = 0, Value = 0;
105 char reg_addr;
106 size_t size;
107 int ret = 0;
108 #ifdef DEBUG
109 /* int i; */
110 #endif
112 dev_info(pTAS2557->dev, "%s\n", __func__);
113 if (count > MAX_LENGTH) {
114 dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH);
115 return -EINVAL;
116 }
118 /* copy register address from user space */
119 size = copy_from_user(®_addr, buf, 1);
120 if (size != 0) {
121 dev_err(pTAS2557->dev, "read: copy_from_user failure\n");
122 return -EINVAL;
123 }
125 size = count;
127 nCompositeRegister = BPR_REG(gBook, gPage, reg_addr);
128 if (count == 1) {
129 ret =
130 pTAS2557->read(pTAS2557, 0x80000000 | nCompositeRegister, &Value);
131 if (ret >= 0)
132 rd_data[0] = (char) Value;
133 } else if (count > 1) {
134 ret =
135 pTAS2557->bulk_read(pTAS2557, 0x80000000 | nCompositeRegister,
136 rd_data, size);
137 }
138 if (ret < 0)
139 dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu error happen!\n",
140 __func__, __LINE__, ret, count);
142 #ifdef DEBUG
143 dev_info(pTAS2557->dev, "read size = %d, reg_addr= %x , count = %d\n",
144 (int) size, reg_addr, (int) count);
145 /* for (i = 0; i < (int) size; i++) {
146 * dev_dbg(pTAS2557->dev, "rd_data[%d]=%x\n", i, rd_data[i]);
147 * }
148 */
149 #endif
150 if (size != count)
151 dev_err(pTAS2557->dev, "read %d registers from the codec\n", (int) size);
153 if (copy_to_user(buf, rd_data, size) != 0) {
154 dev_err(pTAS2557->dev, "copy_to_user failed\n");
155 return -EINVAL;
156 }
158 return size;
159 }
161 /*
162 *----------------------------------------------------------------------------
163 * Function : tiload_write
164 *
165 * Purpose : write to codec
166 *----------------------------------------------------------------------------
167 */
168 static ssize_t tiload_write(struct file *filp, const char __user *buf,
169 size_t count, loff_t *offset)
170 {
171 struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
172 static char wr_data[MAX_LENGTH + 1];
173 char *pData = wr_data;
174 size_t size;
175 unsigned int nCompositeRegister = 0;
176 unsigned int nRegister;
177 int ret = 0;
178 #ifdef DEBUG
179 /* int i; */
180 #endif
181 dev_info(pTAS2557->dev, "%s\n", __func__);
183 if (count > MAX_LENGTH) {
184 dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH);
185 return -EINVAL;
186 }
188 /* copy buffer from user space */
189 size = copy_from_user(wr_data, buf, count);
190 if (size != 0) {
191 dev_err(pTAS2557->dev, "copy_from_user failure %d\n", (int) size);
192 return -EINVAL;
193 }
194 #ifdef DEBUG
195 dev_info(pTAS2557->dev, "write size = %zu\n", count);
196 /* for (i = 0; i < (int) count; i++) {
197 * dev_info(pTAS2557->dev, "wr_data[%d]=%x\n", i, wr_data[i]);
198 * }
199 */
200 #endif
201 nRegister = wr_data[0];
202 size = count;
203 if ((nRegister == 127) && (gPage == 0)) {
204 gBook = wr_data[1];
205 return size;
206 }
208 if (nRegister == 0) {
209 gPage = wr_data[1];
210 pData++;
211 count--;
212 }
214 nCompositeRegister = BPR_REG(gBook, gPage, nRegister);
215 if (count == 2) {
216 ret =
217 pTAS2557->write(pTAS2557, 0x80000000 | nCompositeRegister,
218 pData[1]);
219 } else if (count > 2) {
220 ret =
221 pTAS2557->bulk_write(pTAS2557, 0x80000000 | nCompositeRegister,
222 &pData[1], count - 1);
223 }
225 if (ret < 0)
226 dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu, ERROR Happen\n", __func__,
227 __LINE__, ret, count);
229 return size;
230 }
232 static void tiload_route_IO(struct tas2557_priv *pTAS2557, unsigned int bLock)
233 {
234 if (bLock)
235 pTAS2557->write(pTAS2557, 0xAFFEAFFE, 0xBABEBABE);
236 else
237 pTAS2557->write(pTAS2557, 0xBABEBABE, 0xAFFEAFFE);
238 }
240 static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
241 {
242 struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
243 long num = 0;
244 void __user *argp = (void __user *) arg;
245 int val;
246 struct BPR bpr;
248 dev_info(pTAS2557->dev, "%s, cmd=0x%x\n", __func__, cmd);
249 /* if (_IOC_TYPE(cmd) != TILOAD_IOC_MAGIC)
250 * return -ENOTTY;
251 */
253 switch (cmd) {
254 case TILOAD_IOMAGICNUM_GET:
255 num = copy_to_user(argp, &magic_num, sizeof(int));
256 break;
257 case TILOAD_IOMAGICNUM_SET:
258 num = copy_from_user(&magic_num, argp, sizeof(int));
259 dev_info(pTAS2557->dev, "TILOAD_IOMAGICNUM_SET\n");
260 tiload_route_IO(pTAS2557, magic_num);
261 break;
262 case TILOAD_BPR_READ:
263 break;
264 case TILOAD_BPR_WRITE:
265 num = copy_from_user(&bpr, argp, sizeof(struct BPR));
266 dev_info(pTAS2557->dev, "TILOAD_BPR_WRITE: 0x%02X, 0x%02X, 0x%02X\n\r", bpr.nBook,
267 bpr.nPage, bpr.nRegister);
268 break;
269 case TILOAD_IOCTL_SET_CHL:
270 break;
271 case TILOAD_IOCTL_SET_CONFIG:
272 num = copy_from_user(&val, argp, sizeof(val));
273 pTAS2557->set_config(pTAS2557, val);
274 break;
275 case TILOAD_IOCTL_SET_CALIBRATION:
276 num = copy_from_user(&val, argp, sizeof(val));
277 pTAS2557->set_calibration(pTAS2557, val);
278 break;
279 default:
280 break;
281 }
282 return num;
283 }
285 #ifdef CONFIG_COMPAT
286 static long tiload_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
287 {
288 struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
289 long nResult = 0;
291 switch (cmd) {
292 case TILOAD_COMPAT_IOMAGICNUM_GET:
293 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_GET=0x%x\n",
294 __func__, cmd);
295 nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_GET,
296 (unsigned long) compat_ptr(arg));
297 break;
299 case TILOAD_COMPAT_IOMAGICNUM_SET:
300 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_SET=0x%x\n",
301 __func__, cmd);
302 nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_SET,
303 (unsigned long) compat_ptr(arg));
304 break;
306 case TILOAD_COMPAT_BPR_READ:
307 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_READ=0x%x\n",
308 __func__, cmd);
309 nResult = tiload_ioctl(filp, TILOAD_BPR_READ,
310 (unsigned long) compat_ptr(arg));
311 break;
313 case TILOAD_COMPAT_BPR_WRITE:
314 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_WRITE=0x%x\n",
315 __func__, cmd);
316 nResult = tiload_ioctl(filp, TILOAD_BPR_WRITE,
317 (unsigned long) compat_ptr(arg));
318 break;
320 case TILOAD_COMPAT_IOCTL_SET_CHL:
321 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CHL=0x%x\n",
322 __func__, cmd);
323 nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CHL,
324 (unsigned long) compat_ptr(arg));
325 break;
327 case TILOAD_COMPAT_IOCTL_SET_CONFIG:
328 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CONFIG=0x%x\n",
329 __func__, cmd);
330 nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CONFIG,
331 (unsigned long) compat_ptr(arg));
332 break;
334 case TILOAD_COMPAT_IOCTL_SET_CALIBRATION:
335 dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CALIBRATION=0x%x\n",
336 __func__, cmd);
337 nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CALIBRATION,
338 (unsigned long) compat_ptr(arg));
339 break;
341 default:
342 dev_err(pTAS2557->dev, "%s, unsupport compat ioctl=0x%x\n",
343 __func__, cmd);
344 break;
345 }
347 return nResult;
348 }
349 #endif
351 /*********** File operations structure for tiload *************/
352 static const struct file_operations tiload_fops = {
353 .owner = THIS_MODULE,
354 .open = tiload_open,
355 .release = tiload_release,
356 .read = tiload_read,
357 .write = tiload_write,
358 .unlocked_ioctl = tiload_ioctl,
359 #ifdef CONFIG_COMPAT
360 .compat_ioctl = tiload_compat_ioctl,
361 #endif
362 };
364 /*----------------------------------------------------------------------------
365 * Function : tiload_driver_init
366 *
367 * Purpose : Register a char driver for dynamic tiload programming
368 *----------------------------------------------------------------------------
369 */
370 int tiload_driver_init(struct tas2557_priv *pTAS2557)
371 {
372 int result;
373 dev_t dev = MKDEV(tiload_major, 0);
375 g_TAS2557 = pTAS2557;
377 dev_info(pTAS2557->dev, "%s\n", __func__);
379 result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
380 if (result < 0) {
381 dev_err(pTAS2557->dev, "cannot allocate major number %d\n", tiload_major);
382 return result;
383 }
384 tiload_class = class_create(THIS_MODULE, DEVICE_NAME);
385 tiload_major = MAJOR(dev);
386 dev_info(pTAS2557->dev, "allocated Major Number: %d\n", tiload_major);
388 tiload_cdev = cdev_alloc();
389 cdev_init(tiload_cdev, &tiload_fops);
390 tiload_cdev->owner = THIS_MODULE;
391 tiload_cdev->ops = &tiload_fops;
393 if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL)
394 dev_err(pTAS2557->dev, "Device creation failed\n");
396 if (cdev_add(tiload_cdev, dev, 1) < 0) {
397 dev_err(pTAS2557->dev, "tiload_driver: cdev_add failed\n");
398 unregister_chrdev_region(dev, 1);
399 tiload_cdev = NULL;
400 return 1;
401 }
402 dev_info(pTAS2557->dev, "Registered TiLoad driver, Major number: %d\n", tiload_major);
403 /* class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); */
404 return 0;
405 }
407 MODULE_AUTHOR("Texas Instruments Inc.");
408 MODULE_DESCRIPTION("Utility for TAS2557 Android in-system tuning");
409 MODULE_LICENSE("GPL v2");