18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/***************************************************************************
38c2ecf20Sopenharmony_ci *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
48c2ecf20Sopenharmony_ci *                                                                         *
58c2ecf20Sopenharmony_ci *   Based on Logitech G13 driver (v0.4)                                   *
68c2ecf20Sopenharmony_ci *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
78c2ecf20Sopenharmony_ci *                                                                         *
88c2ecf20Sopenharmony_ci ***************************************************************************/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/hid.h>
118c2ecf20Sopenharmony_ci#include <linux/hid-debug.h>
128c2ecf20Sopenharmony_ci#include <linux/input.h>
138c2ecf20Sopenharmony_ci#include "hid-ids.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/fb.h>
168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/completion.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/string.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "hid-picolcd.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Input device
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
298c2ecf20Sopenharmony_ci * and header for 4x4 key matrix. The built-in keys are part of the matrix.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic const unsigned short def_keymap[PICOLCD_KEYS] = {
328c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* none */
338c2ecf20Sopenharmony_ci	KEY_BACK,	/* col 4 + row 1 */
348c2ecf20Sopenharmony_ci	KEY_HOMEPAGE,	/* col 3 + row 1 */
358c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 2 + row 1 */
368c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 1 + row 1 */
378c2ecf20Sopenharmony_ci	KEY_SCROLLUP,	/* col 4 + row 2 */
388c2ecf20Sopenharmony_ci	KEY_OK,		/* col 3 + row 2 */
398c2ecf20Sopenharmony_ci	KEY_SCROLLDOWN,	/* col 2 + row 2 */
408c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 1 + row 2 */
418c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 4 + row 3 */
428c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 3 + row 3 */
438c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 2 + row 3 */
448c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 1 + row 3 */
458c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 4 + row 4 */
468c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 3 + row 4 */
478c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 2 + row 4 */
488c2ecf20Sopenharmony_ci	KEY_RESERVED,	/* col 1 + row 4 */
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Find a given report */
538c2ecf20Sopenharmony_cistruct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
568c2ecf20Sopenharmony_ci	struct hid_report *report = NULL;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	list_for_each_entry(report, feature_report_list, list) {
598c2ecf20Sopenharmony_ci		if (report->id == id)
608c2ecf20Sopenharmony_ci			return report;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	hid_warn(hdev, "No report with id 0x%x found\n", id);
638c2ecf20Sopenharmony_ci	return NULL;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* Submit a report and wait for a reply from device - if device fades away
678c2ecf20Sopenharmony_ci * or does not respond in time, return NULL */
688c2ecf20Sopenharmony_cistruct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
698c2ecf20Sopenharmony_ci		int report_id, const u8 *raw_data, int size)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct picolcd_data *data = hid_get_drvdata(hdev);
728c2ecf20Sopenharmony_ci	struct picolcd_pending *work;
738c2ecf20Sopenharmony_ci	struct hid_report *report = picolcd_out_report(report_id, hdev);
748c2ecf20Sopenharmony_ci	unsigned long flags;
758c2ecf20Sopenharmony_ci	int i, j, k;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!report || !data)
788c2ecf20Sopenharmony_ci		return NULL;
798c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_FAILED)
808c2ecf20Sopenharmony_ci		return NULL;
818c2ecf20Sopenharmony_ci	work = kzalloc(sizeof(*work), GFP_KERNEL);
828c2ecf20Sopenharmony_ci	if (!work)
838c2ecf20Sopenharmony_ci		return NULL;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	init_completion(&work->ready);
868c2ecf20Sopenharmony_ci	work->out_report = report;
878c2ecf20Sopenharmony_ci	work->in_report  = NULL;
888c2ecf20Sopenharmony_ci	work->raw_size   = 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	mutex_lock(&data->mutex);
918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
928c2ecf20Sopenharmony_ci	for (i = k = 0; i < report->maxfield; i++)
938c2ecf20Sopenharmony_ci		for (j = 0; j < report->field[i]->report_count; j++) {
948c2ecf20Sopenharmony_ci			hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
958c2ecf20Sopenharmony_ci			k++;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_FAILED) {
988c2ecf20Sopenharmony_ci		kfree(work);
998c2ecf20Sopenharmony_ci		work = NULL;
1008c2ecf20Sopenharmony_ci	} else {
1018c2ecf20Sopenharmony_ci		data->pending = work;
1028c2ecf20Sopenharmony_ci		hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
1038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&data->lock, flags);
1048c2ecf20Sopenharmony_ci		wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
1058c2ecf20Sopenharmony_ci		spin_lock_irqsave(&data->lock, flags);
1068c2ecf20Sopenharmony_ci		data->pending = NULL;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
1098c2ecf20Sopenharmony_ci	mutex_unlock(&data->mutex);
1108c2ecf20Sopenharmony_ci	return work;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * input class device
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_cistatic int picolcd_raw_keypad(struct picolcd_data *data,
1178c2ecf20Sopenharmony_ci		struct hid_report *report, u8 *raw_data, int size)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * Keypad event
1218c2ecf20Sopenharmony_ci	 * First and second data bytes list currently pressed keys,
1228c2ecf20Sopenharmony_ci	 * 0x00 means no key and at most 2 keys may be pressed at same time
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	int i, j;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* determine newly pressed keys */
1278c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
1288c2ecf20Sopenharmony_ci		unsigned int key_code;
1298c2ecf20Sopenharmony_ci		if (raw_data[i] == 0)
1308c2ecf20Sopenharmony_ci			continue;
1318c2ecf20Sopenharmony_ci		for (j = 0; j < sizeof(data->pressed_keys); j++)
1328c2ecf20Sopenharmony_ci			if (data->pressed_keys[j] == raw_data[i])
1338c2ecf20Sopenharmony_ci				goto key_already_down;
1348c2ecf20Sopenharmony_ci		for (j = 0; j < sizeof(data->pressed_keys); j++)
1358c2ecf20Sopenharmony_ci			if (data->pressed_keys[j] == 0) {
1368c2ecf20Sopenharmony_ci				data->pressed_keys[j] = raw_data[i];
1378c2ecf20Sopenharmony_ci				break;
1388c2ecf20Sopenharmony_ci			}
1398c2ecf20Sopenharmony_ci		input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
1408c2ecf20Sopenharmony_ci		if (raw_data[i] < PICOLCD_KEYS)
1418c2ecf20Sopenharmony_ci			key_code = data->keycode[raw_data[i]];
1428c2ecf20Sopenharmony_ci		else
1438c2ecf20Sopenharmony_ci			key_code = KEY_UNKNOWN;
1448c2ecf20Sopenharmony_ci		if (key_code != KEY_UNKNOWN) {
1458c2ecf20Sopenharmony_ci			dbg_hid(PICOLCD_NAME " got key press for %u:%d",
1468c2ecf20Sopenharmony_ci					raw_data[i], key_code);
1478c2ecf20Sopenharmony_ci			input_report_key(data->input_keys, key_code, 1);
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		input_sync(data->input_keys);
1508c2ecf20Sopenharmony_cikey_already_down:
1518c2ecf20Sopenharmony_ci		continue;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* determine newly released keys */
1558c2ecf20Sopenharmony_ci	for (j = 0; j < sizeof(data->pressed_keys); j++) {
1568c2ecf20Sopenharmony_ci		unsigned int key_code;
1578c2ecf20Sopenharmony_ci		if (data->pressed_keys[j] == 0)
1588c2ecf20Sopenharmony_ci			continue;
1598c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++)
1608c2ecf20Sopenharmony_ci			if (data->pressed_keys[j] == raw_data[i])
1618c2ecf20Sopenharmony_ci				goto key_still_down;
1628c2ecf20Sopenharmony_ci		input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
1638c2ecf20Sopenharmony_ci		if (data->pressed_keys[j] < PICOLCD_KEYS)
1648c2ecf20Sopenharmony_ci			key_code = data->keycode[data->pressed_keys[j]];
1658c2ecf20Sopenharmony_ci		else
1668c2ecf20Sopenharmony_ci			key_code = KEY_UNKNOWN;
1678c2ecf20Sopenharmony_ci		if (key_code != KEY_UNKNOWN) {
1688c2ecf20Sopenharmony_ci			dbg_hid(PICOLCD_NAME " got key release for %u:%d",
1698c2ecf20Sopenharmony_ci					data->pressed_keys[j], key_code);
1708c2ecf20Sopenharmony_ci			input_report_key(data->input_keys, key_code, 0);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		input_sync(data->input_keys);
1738c2ecf20Sopenharmony_ci		data->pressed_keys[j] = 0;
1748c2ecf20Sopenharmony_cikey_still_down:
1758c2ecf20Sopenharmony_ci		continue;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	return 1;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int picolcd_check_version(struct hid_device *hdev)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct picolcd_data *data = hid_get_drvdata(hdev);
1838c2ecf20Sopenharmony_ci	struct picolcd_pending *verinfo;
1848c2ecf20Sopenharmony_ci	int ret = 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (!data)
1878c2ecf20Sopenharmony_ci		return -ENODEV;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
1908c2ecf20Sopenharmony_ci	if (!verinfo) {
1918c2ecf20Sopenharmony_ci		hid_err(hdev, "no version response from PicoLCD\n");
1928c2ecf20Sopenharmony_ci		return -ENODEV;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (verinfo->raw_size == 2) {
1968c2ecf20Sopenharmony_ci		data->version[0] = verinfo->raw_data[1];
1978c2ecf20Sopenharmony_ci		data->version[1] = verinfo->raw_data[0];
1988c2ecf20Sopenharmony_ci		if (data->status & PICOLCD_BOOTLOADER) {
1998c2ecf20Sopenharmony_ci			hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
2008c2ecf20Sopenharmony_ci				 verinfo->raw_data[1], verinfo->raw_data[0]);
2018c2ecf20Sopenharmony_ci		} else {
2028c2ecf20Sopenharmony_ci			hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
2038c2ecf20Sopenharmony_ci				 verinfo->raw_data[1], verinfo->raw_data[0]);
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci	} else {
2068c2ecf20Sopenharmony_ci		hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
2078c2ecf20Sopenharmony_ci		ret = -EINVAL;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	kfree(verinfo);
2108c2ecf20Sopenharmony_ci	return ret;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*
2148c2ecf20Sopenharmony_ci * Reset our device and wait for answer to VERSION request
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_ciint picolcd_reset(struct hid_device *hdev)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct picolcd_data *data = hid_get_drvdata(hdev);
2198c2ecf20Sopenharmony_ci	struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
2208c2ecf20Sopenharmony_ci	unsigned long flags;
2218c2ecf20Sopenharmony_ci	int error;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!data || !report || report->maxfield != 1)
2248c2ecf20Sopenharmony_ci		return -ENODEV;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
2278c2ecf20Sopenharmony_ci	if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
2288c2ecf20Sopenharmony_ci		data->status |= PICOLCD_BOOTLOADER;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* perform the reset */
2318c2ecf20Sopenharmony_ci	hid_set_field(report->field[0], 0, 1);
2328c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_FAILED) {
2338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&data->lock, flags);
2348c2ecf20Sopenharmony_ci		return -ENODEV;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
2378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	error = picolcd_check_version(hdev);
2408c2ecf20Sopenharmony_ci	if (error)
2418c2ecf20Sopenharmony_ci		return error;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	picolcd_resume_lcd(data);
2448c2ecf20Sopenharmony_ci	picolcd_resume_backlight(data);
2458c2ecf20Sopenharmony_ci	picolcd_fb_refresh(data);
2468c2ecf20Sopenharmony_ci	picolcd_leds_set(data);
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * The "operation_mode" sysfs attribute
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic ssize_t picolcd_operation_mode_show(struct device *dev,
2548c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct picolcd_data *data = dev_get_drvdata(dev);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER)
2598c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
2608c2ecf20Sopenharmony_ci	else
2618c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic ssize_t picolcd_operation_mode_store(struct device *dev,
2658c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct picolcd_data *data = dev_get_drvdata(dev);
2688c2ecf20Sopenharmony_ci	struct hid_report *report = NULL;
2698c2ecf20Sopenharmony_ci	int timeout = data->opmode_delay;
2708c2ecf20Sopenharmony_ci	unsigned long flags;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (sysfs_streq(buf, "lcd")) {
2738c2ecf20Sopenharmony_ci		if (data->status & PICOLCD_BOOTLOADER)
2748c2ecf20Sopenharmony_ci			report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
2758c2ecf20Sopenharmony_ci	} else if (sysfs_streq(buf, "bootloader")) {
2768c2ecf20Sopenharmony_ci		if (!(data->status & PICOLCD_BOOTLOADER))
2778c2ecf20Sopenharmony_ci			report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
2788c2ecf20Sopenharmony_ci	} else {
2798c2ecf20Sopenharmony_ci		return -EINVAL;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!report || report->maxfield != 1)
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
2868c2ecf20Sopenharmony_ci	hid_set_field(report->field[0], 0, timeout & 0xff);
2878c2ecf20Sopenharmony_ci	hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
2888c2ecf20Sopenharmony_ci	hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
2898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
2908c2ecf20Sopenharmony_ci	return count;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
2948c2ecf20Sopenharmony_ci		picolcd_operation_mode_store);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/*
2978c2ecf20Sopenharmony_ci * The "operation_mode_delay" sysfs attribute
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic ssize_t picolcd_operation_mode_delay_show(struct device *dev,
3008c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct picolcd_data *data = dev_get_drvdata(dev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic ssize_t picolcd_operation_mode_delay_store(struct device *dev,
3088c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct picolcd_data *data = dev_get_drvdata(dev);
3118c2ecf20Sopenharmony_ci	unsigned u;
3128c2ecf20Sopenharmony_ci	if (sscanf(buf, "%u", &u) != 1)
3138c2ecf20Sopenharmony_ci		return -EINVAL;
3148c2ecf20Sopenharmony_ci	if (u > 30000)
3158c2ecf20Sopenharmony_ci		return -EINVAL;
3168c2ecf20Sopenharmony_ci	else
3178c2ecf20Sopenharmony_ci		data->opmode_delay = u;
3188c2ecf20Sopenharmony_ci	return count;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
3228c2ecf20Sopenharmony_ci		picolcd_operation_mode_delay_store);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/*
3258c2ecf20Sopenharmony_ci * Handle raw report as sent by device
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_cistatic int picolcd_raw_event(struct hid_device *hdev,
3288c2ecf20Sopenharmony_ci		struct hid_report *report, u8 *raw_data, int size)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct picolcd_data *data = hid_get_drvdata(hdev);
3318c2ecf20Sopenharmony_ci	unsigned long flags;
3328c2ecf20Sopenharmony_ci	int ret = 0;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!data)
3358c2ecf20Sopenharmony_ci		return 1;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (size > 64) {
3388c2ecf20Sopenharmony_ci		hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n",
3398c2ecf20Sopenharmony_ci				size, report->id);
3408c2ecf20Sopenharmony_ci		return 0;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (report->id == REPORT_KEY_STATE) {
3448c2ecf20Sopenharmony_ci		if (data->input_keys)
3458c2ecf20Sopenharmony_ci			ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
3468c2ecf20Sopenharmony_ci	} else if (report->id == REPORT_IR_DATA) {
3478c2ecf20Sopenharmony_ci		ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
3488c2ecf20Sopenharmony_ci	} else {
3498c2ecf20Sopenharmony_ci		spin_lock_irqsave(&data->lock, flags);
3508c2ecf20Sopenharmony_ci		/*
3518c2ecf20Sopenharmony_ci		 * We let the caller of picolcd_send_and_wait() check if the
3528c2ecf20Sopenharmony_ci		 * report we got is one of the expected ones or not.
3538c2ecf20Sopenharmony_ci		 */
3548c2ecf20Sopenharmony_ci		if (data->pending) {
3558c2ecf20Sopenharmony_ci			memcpy(data->pending->raw_data, raw_data+1, size-1);
3568c2ecf20Sopenharmony_ci			data->pending->raw_size  = size-1;
3578c2ecf20Sopenharmony_ci			data->pending->in_report = report;
3588c2ecf20Sopenharmony_ci			complete(&data->pending->ready);
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&data->lock, flags);
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	picolcd_debug_raw_event(data, hdev, report, raw_data, size);
3648c2ecf20Sopenharmony_ci	return 1;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3688c2ecf20Sopenharmony_cistatic int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	if (PMSG_IS_AUTO(message))
3718c2ecf20Sopenharmony_ci		return 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	picolcd_suspend_backlight(hid_get_drvdata(hdev));
3748c2ecf20Sopenharmony_ci	dbg_hid(PICOLCD_NAME " device ready for suspend\n");
3758c2ecf20Sopenharmony_ci	return 0;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int picolcd_resume(struct hid_device *hdev)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int ret;
3818c2ecf20Sopenharmony_ci	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
3828c2ecf20Sopenharmony_ci	if (ret)
3838c2ecf20Sopenharmony_ci		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int picolcd_reset_resume(struct hid_device *hdev)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	int ret;
3908c2ecf20Sopenharmony_ci	ret = picolcd_reset(hdev);
3918c2ecf20Sopenharmony_ci	if (ret)
3928c2ecf20Sopenharmony_ci		dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
3938c2ecf20Sopenharmony_ci	ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
3948c2ecf20Sopenharmony_ci	if (ret)
3958c2ecf20Sopenharmony_ci		dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
3968c2ecf20Sopenharmony_ci	ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
3978c2ecf20Sopenharmony_ci	if (ret)
3988c2ecf20Sopenharmony_ci		dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
3998c2ecf20Sopenharmony_ci	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
4008c2ecf20Sopenharmony_ci	if (ret)
4018c2ecf20Sopenharmony_ci		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
4028c2ecf20Sopenharmony_ci	picolcd_leds_set(hid_get_drvdata(hdev));
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci#endif
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/* initialize keypad input device */
4088c2ecf20Sopenharmony_cistatic int picolcd_init_keys(struct picolcd_data *data,
4098c2ecf20Sopenharmony_ci		struct hid_report *report)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct hid_device *hdev = data->hdev;
4128c2ecf20Sopenharmony_ci	struct input_dev *idev;
4138c2ecf20Sopenharmony_ci	int error, i;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (!report)
4168c2ecf20Sopenharmony_ci		return -ENODEV;
4178c2ecf20Sopenharmony_ci	if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
4188c2ecf20Sopenharmony_ci			report->field[0]->report_size != 8) {
4198c2ecf20Sopenharmony_ci		hid_err(hdev, "unsupported KEY_STATE report\n");
4208c2ecf20Sopenharmony_ci		return -EINVAL;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	idev = input_allocate_device();
4248c2ecf20Sopenharmony_ci	if (idev == NULL) {
4258c2ecf20Sopenharmony_ci		hid_err(hdev, "failed to allocate input device\n");
4268c2ecf20Sopenharmony_ci		return -ENOMEM;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	input_set_drvdata(idev, hdev);
4298c2ecf20Sopenharmony_ci	memcpy(data->keycode, def_keymap, sizeof(def_keymap));
4308c2ecf20Sopenharmony_ci	idev->name = hdev->name;
4318c2ecf20Sopenharmony_ci	idev->phys = hdev->phys;
4328c2ecf20Sopenharmony_ci	idev->uniq = hdev->uniq;
4338c2ecf20Sopenharmony_ci	idev->id.bustype = hdev->bus;
4348c2ecf20Sopenharmony_ci	idev->id.vendor  = hdev->vendor;
4358c2ecf20Sopenharmony_ci	idev->id.product = hdev->product;
4368c2ecf20Sopenharmony_ci	idev->id.version = hdev->version;
4378c2ecf20Sopenharmony_ci	idev->dev.parent = &hdev->dev;
4388c2ecf20Sopenharmony_ci	idev->keycode     = &data->keycode;
4398c2ecf20Sopenharmony_ci	idev->keycodemax  = PICOLCD_KEYS;
4408c2ecf20Sopenharmony_ci	idev->keycodesize = sizeof(data->keycode[0]);
4418c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_MSC, MSC_SCAN);
4428c2ecf20Sopenharmony_ci	set_bit(EV_REP, idev->evbit);
4438c2ecf20Sopenharmony_ci	for (i = 0; i < PICOLCD_KEYS; i++)
4448c2ecf20Sopenharmony_ci		input_set_capability(idev, EV_KEY, data->keycode[i]);
4458c2ecf20Sopenharmony_ci	error = input_register_device(idev);
4468c2ecf20Sopenharmony_ci	if (error) {
4478c2ecf20Sopenharmony_ci		hid_err(hdev, "error registering the input device\n");
4488c2ecf20Sopenharmony_ci		input_free_device(idev);
4498c2ecf20Sopenharmony_ci		return error;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	data->input_keys = idev;
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic void picolcd_exit_keys(struct picolcd_data *data)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct input_dev *idev = data->input_keys;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	data->input_keys = NULL;
4608c2ecf20Sopenharmony_ci	if (idev)
4618c2ecf20Sopenharmony_ci		input_unregister_device(idev);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	int error;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Setup keypad input device */
4698c2ecf20Sopenharmony_ci	error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
4708c2ecf20Sopenharmony_ci	if (error)
4718c2ecf20Sopenharmony_ci		goto err;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Setup CIR input device */
4748c2ecf20Sopenharmony_ci	error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
4758c2ecf20Sopenharmony_ci	if (error)
4768c2ecf20Sopenharmony_ci		goto err;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* Set up the framebuffer device */
4798c2ecf20Sopenharmony_ci	error = picolcd_init_framebuffer(data);
4808c2ecf20Sopenharmony_ci	if (error)
4818c2ecf20Sopenharmony_ci		goto err;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Setup lcd class device */
4848c2ecf20Sopenharmony_ci	error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
4858c2ecf20Sopenharmony_ci	if (error)
4868c2ecf20Sopenharmony_ci		goto err;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* Setup backlight class device */
4898c2ecf20Sopenharmony_ci	error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
4908c2ecf20Sopenharmony_ci	if (error)
4918c2ecf20Sopenharmony_ci		goto err;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Setup the LED class devices */
4948c2ecf20Sopenharmony_ci	error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
4958c2ecf20Sopenharmony_ci	if (error)
4968c2ecf20Sopenharmony_ci		goto err;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
4998c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_EE_WRITE, hdev),
5008c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_READ_MEMORY, hdev),
5018c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
5028c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_RESET, hdev));
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_cierr:
5058c2ecf20Sopenharmony_ci	picolcd_exit_leds(data);
5068c2ecf20Sopenharmony_ci	picolcd_exit_backlight(data);
5078c2ecf20Sopenharmony_ci	picolcd_exit_lcd(data);
5088c2ecf20Sopenharmony_ci	picolcd_exit_framebuffer(data);
5098c2ecf20Sopenharmony_ci	picolcd_exit_cir(data);
5108c2ecf20Sopenharmony_ci	picolcd_exit_keys(data);
5118c2ecf20Sopenharmony_ci	return error;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	picolcd_init_devfs(data, NULL, NULL,
5178c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
5188c2ecf20Sopenharmony_ci			picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
5198c2ecf20Sopenharmony_ci	return 0;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int picolcd_probe(struct hid_device *hdev,
5238c2ecf20Sopenharmony_ci		     const struct hid_device_id *id)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct picolcd_data *data;
5268c2ecf20Sopenharmony_ci	int error = -ENOMEM;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	dbg_hid(PICOLCD_NAME " hardware probe...\n");
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/*
5318c2ecf20Sopenharmony_ci	 * Let's allocate the picolcd data structure, set some reasonable
5328c2ecf20Sopenharmony_ci	 * defaults, and associate it with the device
5338c2ecf20Sopenharmony_ci	 */
5348c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
5358c2ecf20Sopenharmony_ci	if (data == NULL) {
5368c2ecf20Sopenharmony_ci		hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
5378c2ecf20Sopenharmony_ci		return -ENOMEM;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock_init(&data->lock);
5418c2ecf20Sopenharmony_ci	mutex_init(&data->mutex);
5428c2ecf20Sopenharmony_ci	data->hdev = hdev;
5438c2ecf20Sopenharmony_ci	data->opmode_delay = 5000;
5448c2ecf20Sopenharmony_ci	if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
5458c2ecf20Sopenharmony_ci		data->status |= PICOLCD_BOOTLOADER;
5468c2ecf20Sopenharmony_ci	hid_set_drvdata(hdev, data);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Parse the device reports and start it up */
5498c2ecf20Sopenharmony_ci	error = hid_parse(hdev);
5508c2ecf20Sopenharmony_ci	if (error) {
5518c2ecf20Sopenharmony_ci		hid_err(hdev, "device report parse failed\n");
5528c2ecf20Sopenharmony_ci		goto err_cleanup_data;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	error = hid_hw_start(hdev, 0);
5568c2ecf20Sopenharmony_ci	if (error) {
5578c2ecf20Sopenharmony_ci		hid_err(hdev, "hardware start failed\n");
5588c2ecf20Sopenharmony_ci		goto err_cleanup_data;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	error = hid_hw_open(hdev);
5628c2ecf20Sopenharmony_ci	if (error) {
5638c2ecf20Sopenharmony_ci		hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
5648c2ecf20Sopenharmony_ci		goto err_cleanup_hid_hw;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
5688c2ecf20Sopenharmony_ci	if (error) {
5698c2ecf20Sopenharmony_ci		hid_err(hdev, "failed to create sysfs attributes\n");
5708c2ecf20Sopenharmony_ci		goto err_cleanup_hid_ll;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
5748c2ecf20Sopenharmony_ci	if (error) {
5758c2ecf20Sopenharmony_ci		hid_err(hdev, "failed to create sysfs attributes\n");
5768c2ecf20Sopenharmony_ci		goto err_cleanup_sysfs1;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER)
5808c2ecf20Sopenharmony_ci		error = picolcd_probe_bootloader(hdev, data);
5818c2ecf20Sopenharmony_ci	else
5828c2ecf20Sopenharmony_ci		error = picolcd_probe_lcd(hdev, data);
5838c2ecf20Sopenharmony_ci	if (error)
5848c2ecf20Sopenharmony_ci		goto err_cleanup_sysfs2;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	dbg_hid(PICOLCD_NAME " activated and initialized\n");
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cierr_cleanup_sysfs2:
5908c2ecf20Sopenharmony_ci	device_remove_file(&hdev->dev, &dev_attr_operation_mode);
5918c2ecf20Sopenharmony_cierr_cleanup_sysfs1:
5928c2ecf20Sopenharmony_ci	device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
5938c2ecf20Sopenharmony_cierr_cleanup_hid_ll:
5948c2ecf20Sopenharmony_ci	hid_hw_close(hdev);
5958c2ecf20Sopenharmony_cierr_cleanup_hid_hw:
5968c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
5978c2ecf20Sopenharmony_cierr_cleanup_data:
5988c2ecf20Sopenharmony_ci	kfree(data);
5998c2ecf20Sopenharmony_ci	return error;
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic void picolcd_remove(struct hid_device *hdev)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct picolcd_data *data = hid_get_drvdata(hdev);
6058c2ecf20Sopenharmony_ci	unsigned long flags;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	dbg_hid(PICOLCD_NAME " hardware remove...\n");
6088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
6098c2ecf20Sopenharmony_ci	data->status |= PICOLCD_FAILED;
6108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	picolcd_exit_devfs(data);
6138c2ecf20Sopenharmony_ci	device_remove_file(&hdev->dev, &dev_attr_operation_mode);
6148c2ecf20Sopenharmony_ci	device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
6158c2ecf20Sopenharmony_ci	hid_hw_close(hdev);
6168c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Shortcut potential pending reply that will never arrive */
6198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
6208c2ecf20Sopenharmony_ci	if (data->pending)
6218c2ecf20Sopenharmony_ci		complete(&data->pending->ready);
6228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* Cleanup LED */
6258c2ecf20Sopenharmony_ci	picolcd_exit_leds(data);
6268c2ecf20Sopenharmony_ci	/* Clean up the framebuffer */
6278c2ecf20Sopenharmony_ci	picolcd_exit_backlight(data);
6288c2ecf20Sopenharmony_ci	picolcd_exit_lcd(data);
6298c2ecf20Sopenharmony_ci	picolcd_exit_framebuffer(data);
6308c2ecf20Sopenharmony_ci	/* Cleanup input */
6318c2ecf20Sopenharmony_ci	picolcd_exit_cir(data);
6328c2ecf20Sopenharmony_ci	picolcd_exit_keys(data);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	mutex_destroy(&data->mutex);
6358c2ecf20Sopenharmony_ci	/* Finally, clean up the picolcd data itself */
6368c2ecf20Sopenharmony_ci	kfree(data);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic const struct hid_device_id picolcd_devices[] = {
6408c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
6418c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
6428c2ecf20Sopenharmony_ci	{ }
6438c2ecf20Sopenharmony_ci};
6448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, picolcd_devices);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic struct hid_driver picolcd_driver = {
6478c2ecf20Sopenharmony_ci	.name =          "hid-picolcd",
6488c2ecf20Sopenharmony_ci	.id_table =      picolcd_devices,
6498c2ecf20Sopenharmony_ci	.probe =         picolcd_probe,
6508c2ecf20Sopenharmony_ci	.remove =        picolcd_remove,
6518c2ecf20Sopenharmony_ci	.raw_event =     picolcd_raw_event,
6528c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
6538c2ecf20Sopenharmony_ci	.suspend =       picolcd_suspend,
6548c2ecf20Sopenharmony_ci	.resume =        picolcd_resume,
6558c2ecf20Sopenharmony_ci	.reset_resume =  picolcd_reset_resume,
6568c2ecf20Sopenharmony_ci#endif
6578c2ecf20Sopenharmony_ci};
6588c2ecf20Sopenharmony_cimodule_hid_driver(picolcd_driver);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
6618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
662