1 // SPDX-License-Identifier: GPL-2.0-only
3 * Picvue PVC160206 display driver
5 * Brian Murphy <brian.murphy@eicon.com>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/errno.h>
14 #include <linux/proc_fs.h>
15 #include <linux/seq_file.h>
16 #include <linux/interrupt.h>
18 #include <linux/timer.h>
19 #include <linux/mutex.h>
20 #include <linux/uaccess.h>
24 static DEFINE_MUTEX(pvc_mutex
);
25 static char pvc_lines
[PVC_NLINES
][PVC_LINELEN
+1];
26 static int pvc_linedata
[PVC_NLINES
];
27 static char *pvc_linename
[PVC_NLINES
] = {"line1", "line2"};
28 #define DISPLAY_DIR_NAME "display"
29 static int scroll_dir
, scroll_interval
;
31 static struct timer_list timer
;
33 static void pvc_display(unsigned long data
)
38 for (i
= 0; i
< PVC_NLINES
; i
++)
39 pvc_write_string(pvc_lines
[i
], 0, i
);
42 static DECLARE_TASKLET(pvc_display_tasklet
, &pvc_display
, 0);
44 static int pvc_line_proc_show(struct seq_file
*m
, void *v
)
46 int lineno
= *(int *)m
->private;
48 if (lineno
< 0 || lineno
>= PVC_NLINES
) {
49 printk(KERN_WARNING
"proc_read_line: invalid lineno %d\n", lineno
);
53 mutex_lock(&pvc_mutex
);
54 seq_printf(m
, "%s\n", pvc_lines
[lineno
]);
55 mutex_unlock(&pvc_mutex
);
60 static int pvc_line_proc_open(struct inode
*inode
, struct file
*file
)
62 return single_open(file
, pvc_line_proc_show
, PDE_DATA(inode
));
65 static ssize_t
pvc_line_proc_write(struct file
*file
, const char __user
*buf
,
66 size_t count
, loff_t
*pos
)
68 int lineno
= *(int *)PDE_DATA(file_inode(file
));
69 char kbuf
[PVC_LINELEN
];
72 BUG_ON(lineno
< 0 || lineno
>= PVC_NLINES
);
74 len
= min(count
, sizeof(kbuf
) - 1);
75 if (copy_from_user(kbuf
, buf
, len
))
79 if (len
> 0 && kbuf
[len
- 1] == '\n')
82 mutex_lock(&pvc_mutex
);
83 strncpy(pvc_lines
[lineno
], kbuf
, len
);
84 pvc_lines
[lineno
][len
] = '\0';
85 mutex_unlock(&pvc_mutex
);
87 tasklet_schedule(&pvc_display_tasklet
);
92 static const struct proc_ops pvc_line_proc_ops
= {
93 .proc_open
= pvc_line_proc_open
,
94 .proc_read
= seq_read
,
95 .proc_lseek
= seq_lseek
,
96 .proc_release
= single_release
,
97 .proc_write
= pvc_line_proc_write
,
100 static ssize_t
pvc_scroll_proc_write(struct file
*file
, const char __user
*buf
,
101 size_t count
, loff_t
*pos
)
107 len
= min(count
, sizeof(kbuf
) - 1);
108 if (copy_from_user(kbuf
, buf
, len
))
112 cmd
= simple_strtol(kbuf
, NULL
, 10);
114 mutex_lock(&pvc_mutex
);
115 if (scroll_interval
!= 0)
124 scroll_interval
= -cmd
;
127 scroll_interval
= cmd
;
131 mutex_unlock(&pvc_mutex
);
136 static int pvc_scroll_proc_show(struct seq_file
*m
, void *v
)
138 mutex_lock(&pvc_mutex
);
139 seq_printf(m
, "%d\n", scroll_dir
* scroll_interval
);
140 mutex_unlock(&pvc_mutex
);
145 static int pvc_scroll_proc_open(struct inode
*inode
, struct file
*file
)
147 return single_open(file
, pvc_scroll_proc_show
, NULL
);
150 static const struct proc_ops pvc_scroll_proc_ops
= {
151 .proc_open
= pvc_scroll_proc_open
,
152 .proc_read
= seq_read
,
153 .proc_lseek
= seq_lseek
,
154 .proc_release
= single_release
,
155 .proc_write
= pvc_scroll_proc_write
,
158 void pvc_proc_timerfunc(struct timer_list
*unused
)
161 pvc_move(DISPLAY
|RIGHT
);
162 else if (scroll_dir
> 0)
163 pvc_move(DISPLAY
|LEFT
);
165 timer
.expires
= jiffies
+ scroll_interval
;
169 static void pvc_proc_cleanup(void)
171 remove_proc_subtree(DISPLAY_DIR_NAME
, NULL
);
172 del_timer_sync(&timer
);
175 static int __init
pvc_proc_init(void)
177 struct proc_dir_entry
*dir
, *proc_entry
;
180 dir
= proc_mkdir(DISPLAY_DIR_NAME
, NULL
);
184 for (i
= 0; i
< PVC_NLINES
; i
++) {
185 strcpy(pvc_lines
[i
], "");
188 for (i
= 0; i
< PVC_NLINES
; i
++) {
189 proc_entry
= proc_create_data(pvc_linename
[i
], 0644, dir
,
190 &pvc_line_proc_ops
, &pvc_linedata
[i
]);
191 if (proc_entry
== NULL
)
194 proc_entry
= proc_create("scroll", 0644, dir
, &pvc_scroll_proc_ops
);
195 if (proc_entry
== NULL
)
198 timer_setup(&timer
, pvc_proc_timerfunc
, 0);
206 module_init(pvc_proc_init
);
207 module_exit(pvc_proc_cleanup
);
208 MODULE_LICENSE("GPL");