lcd_2x20.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/i2c.h>
  5. #include <linux/uaccess.h>
  6. #include <linux/miscdevice.h>
  7. #include <linux/pci.h>
  8. #include <linux/mutex.h>
  9. #include <linux/delay.h>
  10. #include <linux/slab.h>
  11. #include <linux/string.h>
  12. #define DRIVER_NAME "lcd2002"
  13. #define LCD_I2C_ADDR_DEFAULT 0x3E
  14. #define LCD_CONTROL_BYTE 0x00
  15. #define LCD_DATA_BYTE 0x40
  16. #define LCD_CLEAR_DISPLAY 0x01
  17. #define LCD_RETURN_HOME 0x02
  18. #define LCD_ENTRY_MODE_SET 0x04
  19. #define LCD_DISPLAY_CONTROL 0x08
  20. #define LCD_CURSOR_SHIFT 0x10
  21. #define LCD_FUNCTION_SET 0x20
  22. #define LCD_SET_CGRAM_ADDR 0x40
  23. #define LCD_SET_DDRAM_ADDR 0x80
  24. /* Entry Mode */
  25. #define LCD_ENTRY_RIGHT 0x00
  26. #define LCD_ENTRY_LEFT 0x02
  27. /* Display Control */
  28. #define LCD_DISPLAY_ON 0x04
  29. #define LCD_DISPLAY_OFF 0x00
  30. #define LCD_CURSOR_ON 0x02
  31. #define LCD_CURSOR_OFF 0x00
  32. #define LCD_BLINK_ON 0x01
  33. #define LCD_BLINK_OFF 0x00
  34. #define LCD_LINE1_ADDR 0x00
  35. #define LCD_LINE2_ADDR 0x40
  36. #define LCD_COLS 20
  37. #define LCD_ROWS 2
  38. /* IOCTL */
  39. #define LCD_IOCTL_MAGIC 'L'
  40. #define LCD_IOCTL_CLEAR _IO(LCD_IOCTL_MAGIC, 0)
  41. #define LCD_IOCTL_HOME _IO(LCD_IOCTL_MAGIC, 1)
  42. #define LCD_IOCTL_SET_CURSOR _IOW(LCD_IOCTL_MAGIC, 2, struct lcd_pos)
  43. #define LCD_IOCTL_DISPLAY_CTRL _IOW(LCD_IOCTL_MAGIC, 3, unsigned char[3])
  44. struct lcd_pos {
  45. unsigned char col;
  46. unsigned char row;
  47. };
  48. struct lcd2002_dev {
  49. struct i2c_adapter *adapter;
  50. struct mutex lock;
  51. unsigned char addr;
  52. };
  53. static struct lcd2002_dev *lcd_dev = NULL;
  54. static char *pci_addr = "0000:00:15.0";
  55. module_param(pci_addr, charp, 0444);
  56. MODULE_PARM_DESC(pci_addr, "PCI address (e.g., 0000:00:15.0)");
  57. static char *adapter_name = NULL;
  58. module_param(adapter_name, charp, 0444);
  59. MODULE_PARM_DESC(adapter_name, "I2C adapter name (e.g., Synopsys DesignWare)");
  60. static int i2c_bus = -1;
  61. module_param(i2c_bus, int, 0444);
  62. MODULE_PARM_DESC(i2c_bus, "I2C bus number (e.g., 1 for /dev/i2c-1)");
  63. static unsigned char i2c_address = LCD_I2C_ADDR_DEFAULT;
  64. module_param(i2c_address, byte, 0444);
  65. MODULE_PARM_DESC(i2c_address, "LCD I2C address (default: 0x3E)");
  66. static int debug = 1;
  67. module_param(debug, int, 0644);
  68. MODULE_PARM_DESC(debug, "Debug level (0=none, 1=normal, 2=verbose)");
  69. static inline void lcd_delay_ms(unsigned int ms)
  70. {
  71. msleep(ms);
  72. }
  73. static int lcd_i2c_write(unsigned char *buf, int len)
  74. {
  75. struct i2c_msg msg;
  76. int ret;
  77. if (!lcd_dev || !lcd_dev->adapter)
  78. return -ENODEV;
  79. msg.addr = lcd_dev->addr;
  80. msg.flags = 0;
  81. msg.len = len;
  82. msg.buf = buf;
  83. ret = i2c_transfer(lcd_dev->adapter, &msg, 1);
  84. if (ret != 1) {
  85. dev_err(&lcd_dev->adapter->dev, "I2C write failed: %d\n", ret);
  86. return ret < 0 ? ret : -EIO;
  87. }
  88. return 0;
  89. }
  90. static int lcd_send_command(unsigned char cmd)
  91. {
  92. unsigned char buf[2];
  93. int ret;
  94. buf[0] = LCD_CONTROL_BYTE;
  95. buf[1] = cmd;
  96. mutex_lock(&lcd_dev->lock);
  97. ret = lcd_i2c_write(buf, 2);
  98. mutex_unlock(&lcd_dev->lock);
  99. if (ret == 0)
  100. lcd_delay_ms(2);
  101. return ret;
  102. }
  103. static int lcd_send_data(unsigned char data)
  104. {
  105. unsigned char buf[2];
  106. int ret;
  107. buf[0] = LCD_DATA_BYTE;
  108. buf[1] = data;
  109. mutex_lock(&lcd_dev->lock);
  110. ret = lcd_i2c_write(buf, 2);
  111. mutex_unlock(&lcd_dev->lock);
  112. if (ret == 0)
  113. lcd_delay_ms(1);
  114. return ret;
  115. }
  116. static int lcd_init_display(void)
  117. {
  118. int ret;
  119. lcd_delay_ms(50);
  120. ret = lcd_send_command(LCD_FUNCTION_SET | 0x08);
  121. if (ret < 0) return ret;
  122. lcd_delay_ms(5);
  123. ret = lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF);
  124. if (ret < 0) return ret;
  125. ret = lcd_send_command(LCD_CLEAR_DISPLAY);
  126. if (ret < 0) return ret;
  127. lcd_delay_ms(3);
  128. ret = lcd_send_command(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT);
  129. return ret;
  130. }
  131. static int lcd_set_cursor(unsigned char col, unsigned char row)
  132. {
  133. unsigned char addr;
  134. if (col >= LCD_COLS || row >= LCD_ROWS)
  135. return -EINVAL;
  136. addr = (row == 0) ? (LCD_LINE1_ADDR + col) : (LCD_LINE2_ADDR + col);
  137. return lcd_send_command(LCD_SET_DDRAM_ADDR | addr);
  138. }
  139. static int lcd_clear(void)
  140. {
  141. int ret = lcd_send_command(LCD_CLEAR_DISPLAY);
  142. if (ret == 0) lcd_delay_ms(2);
  143. return ret;
  144. }
  145. static int lcd_display_control(unsigned char display, unsigned char cursor, unsigned char blink)
  146. {
  147. return lcd_send_command(LCD_DISPLAY_CONTROL | display | cursor | blink);
  148. }
  149. static int lcd_write_string(const char *str, size_t len)
  150. {
  151. size_t i;
  152. int ret = 0;
  153. unsigned char row = 0, col = 0;
  154. for (i = 0; i < len; i++) {
  155. char c = str[i];
  156. if (c == '\n') {
  157. row = (row + 1) % LCD_ROWS;
  158. col = 0;
  159. ret = lcd_set_cursor(col, row);
  160. } else if (c == '\r') {
  161. col = 0;
  162. ret = lcd_set_cursor(col, row);
  163. } else if (c == '\f') {
  164. ret = lcd_clear();
  165. row = col = 0;
  166. } else {
  167. if (col >= LCD_COLS) {
  168. row = (row + 1) % LCD_ROWS;
  169. col = 0;
  170. ret = lcd_set_cursor(col, row);
  171. if (ret < 0) break;
  172. }
  173. ret = lcd_send_data(c);
  174. col++;
  175. }
  176. if (ret < 0) break;
  177. }
  178. return ret;
  179. }
  180. static void print_adapter_info(struct i2c_adapter *adap, int nr)
  181. {
  182. struct device *parent;
  183. char pci_str[64] = "N/A";
  184. if (!debug) return;
  185. pr_info(" [%d] %s (class: 0x%lx)\n", nr, adap->name, adap->class);
  186. parent = adap->dev.parent;
  187. while (parent) {
  188. if (parent->bus == &pci_bus_type) {
  189. struct pci_dev *pdev = to_pci_dev(parent);
  190. snprintf(pci_str, sizeof(pci_str), "%04x:%02x:%02x.%x",
  191. pci_domain_nr(pdev->bus),
  192. pdev->bus->number,
  193. PCI_SLOT(pdev->devfn),
  194. PCI_FUNC(pdev->devfn));
  195. pr_info(" -> PCI: %s [%s]\n", pci_str, dev_name(parent));
  196. break;
  197. } else {
  198. pr_info(" -> %s [%s]\n", dev_name(parent), parent->bus ? parent->bus->name : "no-bus");
  199. }
  200. parent = parent->parent;
  201. }
  202. }
  203. static struct i2c_adapter *find_by_pci(const char *target_pci)
  204. {
  205. struct i2c_adapter *adapter;
  206. int nr = 0;
  207. bool found = false;
  208. pr_info("Searching for PCI %s...\n", target_pci);
  209. while ((adapter = i2c_get_adapter(nr))) {
  210. struct device *parent = adapter->dev.parent;
  211. char pci_buf[32] = "";
  212. if (debug >= 2)
  213. print_adapter_info(adapter, nr);
  214. while (parent && !found) {
  215. if (parent->bus == &pci_bus_type) {
  216. struct pci_dev *pdev = to_pci_dev(parent);
  217. snprintf(pci_buf, sizeof(pci_buf), "%04x:%02x:%02x.%x",
  218. pci_domain_nr(pdev->bus),
  219. pdev->bus->number,
  220. PCI_SLOT(pdev->devfn),
  221. PCI_FUNC(pdev->devfn));
  222. if (strcmp(pci_buf, target_pci) == 0) {
  223. pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
  224. found = true;
  225. break;
  226. }
  227. break;
  228. }
  229. parent = parent->parent;
  230. }
  231. if (found)
  232. return adapter;
  233. i2c_put_adapter(adapter);
  234. nr++;
  235. }
  236. return NULL;
  237. }
  238. static struct i2c_adapter *find_by_name(const char *name)
  239. {
  240. struct i2c_adapter *adapter;
  241. int nr = 0;
  242. pr_info("Searching for adapter name '%s'...\n", name);
  243. while ((adapter = i2c_get_adapter(nr))) {
  244. if (debug >= 2)
  245. print_adapter_info(adapter, nr);
  246. if (strstr(adapter->name, name)) {
  247. pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
  248. return adapter;
  249. }
  250. i2c_put_adapter(adapter);
  251. nr++;
  252. }
  253. return NULL;
  254. }
  255. static struct i2c_adapter *find_by_bus(int bus)
  256. {
  257. pr_info("Trying i2c bus %d...\n", bus);
  258. return i2c_get_adapter(bus);
  259. }
  260. static struct i2c_adapter *find_adapter(void)
  261. {
  262. struct i2c_adapter *adapter = NULL;
  263. if (i2c_bus >= 0) {
  264. adapter = find_by_bus(i2c_bus);
  265. if (adapter) goto done;
  266. pr_warn("Specified i2c_bus %d not found\n", i2c_bus);
  267. }
  268. if (pci_addr && strlen(pci_addr) > 0) {
  269. adapter = find_by_pci(pci_addr);
  270. if (adapter) goto done;
  271. pr_warn("PCI address %s not found\n", pci_addr);
  272. }
  273. if (adapter_name && strlen(adapter_name) > 0) {
  274. adapter = find_by_name(adapter_name);
  275. if (adapter) goto done;
  276. pr_warn("Adapter name '%s' not found\n", adapter_name);
  277. }
  278. pr_info("Trying auto-detect (Synopsys DesignWare)...\n");
  279. adapter = find_by_name("Synopsys DesignWare");
  280. if (adapter) goto done;
  281. pr_info("Trying fallback i2c-1...\n");
  282. adapter = find_by_bus(1);
  283. done:
  284. return adapter;
  285. }
  286. static int lcd_open(struct inode *inode, struct file *file)
  287. {
  288. return lcd_dev ? 0 : -ENODEV;
  289. }
  290. static int lcd_release(struct inode *inode, struct file *file)
  291. {
  292. return 0;
  293. }
  294. static ssize_t lcd_write(struct file *file, const char __user *buf,
  295. size_t count, loff_t *ppos)
  296. {
  297. char *kbuf;
  298. int ret;
  299. if (count == 0) return 0;
  300. if (count > 256) count = 256;
  301. kbuf = kmalloc(count + 1, GFP_KERNEL);
  302. if (!kbuf) return -ENOMEM;
  303. if (copy_from_user(kbuf, buf, count)) {
  304. kfree(kbuf);
  305. return -EFAULT;
  306. }
  307. kbuf[count] = '\0';
  308. ret = lcd_write_string(kbuf, count);
  309. kfree(kbuf);
  310. return ret < 0 ? ret : count;
  311. }
  312. static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  313. {
  314. int ret = 0;
  315. switch (cmd) {
  316. case LCD_IOCTL_CLEAR:
  317. ret = lcd_clear();
  318. break;
  319. case LCD_IOCTL_HOME:
  320. ret = lcd_send_command(LCD_RETURN_HOME);
  321. if (ret == 0) lcd_delay_ms(2);
  322. break;
  323. case LCD_IOCTL_SET_CURSOR: {
  324. struct lcd_pos pos;
  325. if (copy_from_user(&pos, (void __user *)arg, sizeof(pos)))
  326. return -EFAULT;
  327. ret = lcd_set_cursor(pos.col, pos.row);
  328. break;
  329. }
  330. case LCD_IOCTL_DISPLAY_CTRL: {
  331. unsigned char ctrl[3];
  332. if (copy_from_user(ctrl, (void __user *)arg, sizeof(ctrl)))
  333. return -EFAULT;
  334. ret = lcd_display_control(ctrl[0], ctrl[1], ctrl[2]);
  335. break;
  336. }
  337. default:
  338. ret = -EINVAL;
  339. }
  340. return ret;
  341. }
  342. static const struct file_operations lcd_fops = {
  343. .owner = THIS_MODULE,
  344. .open = lcd_open,
  345. .release = lcd_release,
  346. .write = lcd_write,
  347. .unlocked_ioctl = lcd_ioctl,
  348. .compat_ioctl = lcd_ioctl,
  349. };
  350. static struct miscdevice lcd_miscdev = {
  351. .minor = MISC_DYNAMIC_MINOR,
  352. .name = "lcd2002",
  353. .fops = &lcd_fops,
  354. .mode = 0666,
  355. };
  356. int lcd2x20_init(void)
  357. {
  358. int ret;
  359. pr_info("LCD2002 driver loading (PCI:%s, Bus:%d, Addr:0x%02X)...\n",
  360. pci_addr ? pci_addr : "none", i2c_bus, i2c_address);
  361. lcd_dev = kzalloc(sizeof(*lcd_dev), GFP_KERNEL);
  362. if (!lcd_dev)
  363. return -ENOMEM;
  364. mutex_init(&lcd_dev->lock);
  365. lcd_dev->addr = i2c_address;
  366. lcd_dev->adapter = find_adapter();
  367. if (!lcd_dev->adapter) {
  368. pr_err("Failed to find any suitable I2C adapter!\n");
  369. pr_err("Available adapters (from i2cdetect -l):\n");
  370. {
  371. int nr = 0;
  372. struct i2c_adapter *a;
  373. while ((a = i2c_get_adapter(nr++))) {
  374. pr_err(" i2c-%d: %s\n", nr-1, a->name);
  375. i2c_put_adapter(a);
  376. }
  377. }
  378. ret = -ENODEV;
  379. goto err_free;
  380. }
  381. pr_info("Using I2C adapter: %s\n", lcd_dev->adapter->name);
  382. ret = lcd_init_display();
  383. if (ret < 0) {
  384. pr_err("LCD init failed: %d\n", ret);
  385. goto err_put;
  386. }
  387. ret = misc_register(&lcd_miscdev);
  388. if (ret < 0) {
  389. pr_err("Misc register failed: %d\n", ret);
  390. goto err_put;
  391. }
  392. pr_info("LCD2002 ready at /dev/%s\n", lcd_miscdev.name);
  393. lcd_clear();
  394. lcd_write_string("LCD2002 Driver OK", 17);
  395. lcd_set_cursor(0, 1);
  396. lcd_write_string("Intel ADL-N97", 13);
  397. return 0;
  398. err_put:
  399. i2c_put_adapter(lcd_dev->adapter);
  400. err_free:
  401. kfree(lcd_dev);
  402. lcd_dev = NULL;
  403. return ret;
  404. }
  405. void lcd2x20_exit(void)
  406. {
  407. if (!lcd_dev) return;
  408. misc_deregister(&lcd_miscdev);
  409. lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF);
  410. if (lcd_dev->adapter)
  411. i2c_put_adapter(lcd_dev->adapter);
  412. kfree(lcd_dev);
  413. lcd_dev = NULL;
  414. pr_info("LCD2002 driver unloaded\n");
  415. }