v4.19.13 snapshot.
diff --git a/arch/mips/lasat/picvue_proc.c b/arch/mips/lasat/picvue_proc.c
new file mode 100644
index 0000000..5d89e1e
--- /dev/null
+++ b/arch/mips/lasat/picvue_proc.c
@@ -0,0 +1,210 @@
+/*
+ * Picvue PVC160206 display driver
+ *
+ * Brian Murphy <brian.murphy@eicon.com>
+ *
+ */
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+#include "picvue.h"
+
+static DEFINE_MUTEX(pvc_mutex);
+static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
+static int pvc_linedata[PVC_NLINES];
+static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
+#define DISPLAY_DIR_NAME "display"
+static int scroll_dir, scroll_interval;
+
+static struct timer_list timer;
+
+static void pvc_display(unsigned long data)
+{
+	int i;
+
+	pvc_clear();
+	for (i = 0; i < PVC_NLINES; i++)
+		pvc_write_string(pvc_lines[i], 0, i);
+}
+
+static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0);
+
+static int pvc_line_proc_show(struct seq_file *m, void *v)
+{
+	int lineno = *(int *)m->private;
+
+	if (lineno < 0 || lineno >= PVC_NLINES) {
+		printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno);
+		return 0;
+	}
+
+	mutex_lock(&pvc_mutex);
+	seq_printf(m, "%s\n", pvc_lines[lineno]);
+	mutex_unlock(&pvc_mutex);
+
+	return 0;
+}
+
+static int pvc_line_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pvc_line_proc_show, PDE_DATA(inode));
+}
+
+static ssize_t pvc_line_proc_write(struct file *file, const char __user *buf,
+				   size_t count, loff_t *pos)
+{
+	int lineno = *(int *)PDE_DATA(file_inode(file));
+	char kbuf[PVC_LINELEN];
+	size_t len;
+
+	BUG_ON(lineno < 0 || lineno >= PVC_NLINES);
+
+	len = min(count, sizeof(kbuf) - 1);
+	if (copy_from_user(kbuf, buf, len))
+		return -EFAULT;
+	kbuf[len] = '\0';
+
+	if (len > 0 && kbuf[len - 1] == '\n')
+		len--;
+
+	mutex_lock(&pvc_mutex);
+	strncpy(pvc_lines[lineno], kbuf, len);
+	pvc_lines[lineno][len] = '\0';
+	mutex_unlock(&pvc_mutex);
+
+	tasklet_schedule(&pvc_display_tasklet);
+
+	return count;
+}
+
+static const struct file_operations pvc_line_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= pvc_line_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.write		= pvc_line_proc_write,
+};
+
+static ssize_t pvc_scroll_proc_write(struct file *file, const char __user *buf,
+				     size_t count, loff_t *pos)
+{
+	char kbuf[42];
+	size_t len;
+	int cmd;
+
+	len = min(count, sizeof(kbuf) - 1);
+	if (copy_from_user(kbuf, buf, len))
+		return -EFAULT;
+	kbuf[len] = '\0';
+
+	cmd = simple_strtol(kbuf, NULL, 10);
+
+	mutex_lock(&pvc_mutex);
+	if (scroll_interval != 0)
+		del_timer(&timer);
+
+	if (cmd == 0) {
+		scroll_dir = 0;
+		scroll_interval = 0;
+	} else {
+		if (cmd < 0) {
+			scroll_dir = -1;
+			scroll_interval = -cmd;
+		} else {
+			scroll_dir = 1;
+			scroll_interval = cmd;
+		}
+		add_timer(&timer);
+	}
+	mutex_unlock(&pvc_mutex);
+
+	return count;
+}
+
+static int pvc_scroll_proc_show(struct seq_file *m, void *v)
+{
+	mutex_lock(&pvc_mutex);
+	seq_printf(m, "%d\n", scroll_dir * scroll_interval);
+	mutex_unlock(&pvc_mutex);
+
+	return 0;
+}
+
+static int pvc_scroll_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pvc_scroll_proc_show, NULL);
+}
+
+static const struct file_operations pvc_scroll_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= pvc_scroll_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.write		= pvc_scroll_proc_write,
+};
+
+void pvc_proc_timerfunc(struct timer_list *unused)
+{
+	if (scroll_dir < 0)
+		pvc_move(DISPLAY|RIGHT);
+	else if (scroll_dir > 0)
+		pvc_move(DISPLAY|LEFT);
+
+	timer.expires = jiffies + scroll_interval;
+	add_timer(&timer);
+}
+
+static void pvc_proc_cleanup(void)
+{
+	remove_proc_subtree(DISPLAY_DIR_NAME, NULL);
+	del_timer_sync(&timer);
+}
+
+static int __init pvc_proc_init(void)
+{
+	struct proc_dir_entry *dir, *proc_entry;
+	int i;
+
+	dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
+	if (dir == NULL)
+		goto error;
+
+	for (i = 0; i < PVC_NLINES; i++) {
+		strcpy(pvc_lines[i], "");
+		pvc_linedata[i] = i;
+	}
+	for (i = 0; i < PVC_NLINES; i++) {
+		proc_entry = proc_create_data(pvc_linename[i], 0644, dir,
+					&pvc_line_proc_fops, &pvc_linedata[i]);
+		if (proc_entry == NULL)
+			goto error;
+	}
+	proc_entry = proc_create("scroll", 0644, dir,
+				 &pvc_scroll_proc_fops);
+	if (proc_entry == NULL)
+		goto error;
+
+	timer_setup(&timer, pvc_proc_timerfunc, 0);
+
+	return 0;
+error:
+	pvc_proc_cleanup();
+	return -ENOMEM;
+}
+
+module_init(pvc_proc_init);
+module_exit(pvc_proc_cleanup);
+MODULE_LICENSE("GPL");