18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PlayStation 1/2 joypads via SPI interface Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Tomohiro Yoshidomi <sylph23k@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * PlayStation 1/2 joypad's plug (not socket)
88c2ecf20Sopenharmony_ci *  123 456 789
98c2ecf20Sopenharmony_ci * (...|...|...)
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * 1: DAT -> MISO (pullup with 1k owm to 3.3V)
128c2ecf20Sopenharmony_ci * 2: CMD -> MOSI
138c2ecf20Sopenharmony_ci * 3: 9V (for motor, if not use N.C.)
148c2ecf20Sopenharmony_ci * 4: GND
158c2ecf20Sopenharmony_ci * 5: 3.3V
168c2ecf20Sopenharmony_ci * 6: Attention -> CS(SS)
178c2ecf20Sopenharmony_ci * 7: SCK -> SCK
188c2ecf20Sopenharmony_ci * 8: N.C.
198c2ecf20Sopenharmony_ci * 9: ACK -> N.C.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/device.h>
248c2ecf20Sopenharmony_ci#include <linux/input.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
278c2ecf20Sopenharmony_ci#include <linux/types.h>
288c2ecf20Sopenharmony_ci#include <linux/pm.h>
298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | \
328c2ecf20Sopenharmony_ci	(((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | \
338c2ecf20Sopenharmony_ci	(((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7))
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* PlayStation 1/2 joypad command and response are LSBFIRST. */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci *	0x01, 0x42, 0x00, 0x00, 0x00,
398c2ecf20Sopenharmony_ci *	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408c2ecf20Sopenharmony_ci *	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistatic const u8 PSX_CMD_POLL[] = {
438c2ecf20Sopenharmony_ci	0x80, 0x42, 0x00, 0x00, 0x00,
448c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci/*	0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 */
488c2ecf20Sopenharmony_cistatic const u8 PSX_CMD_ENTER_CFG[] = {
498c2ecf20Sopenharmony_ci	0x80, 0xC2, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci/*	0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A */
528c2ecf20Sopenharmony_cistatic const u8 PSX_CMD_EXIT_CFG[] = {
538c2ecf20Sopenharmony_ci	0x80, 0xC2, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci/*	0x01, 0x4D, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF */
568c2ecf20Sopenharmony_cistatic const u8 PSX_CMD_ENABLE_MOTOR[]	= {
578c2ecf20Sopenharmony_ci	0x80, 0xB2, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct psxpad {
618c2ecf20Sopenharmony_ci	struct spi_device *spi;
628c2ecf20Sopenharmony_ci	struct input_dev *idev;
638c2ecf20Sopenharmony_ci	char phys[0x20];
648c2ecf20Sopenharmony_ci	bool motor1enable;
658c2ecf20Sopenharmony_ci	bool motor2enable;
668c2ecf20Sopenharmony_ci	u8 motor1level;
678c2ecf20Sopenharmony_ci	u8 motor2level;
688c2ecf20Sopenharmony_ci	u8 sendbuf[0x20] ____cacheline_aligned;
698c2ecf20Sopenharmony_ci	u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int psxpad_command(struct psxpad *pad, const u8 sendcmdlen)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct spi_transfer xfers = {
758c2ecf20Sopenharmony_ci		.tx_buf		= pad->sendbuf,
768c2ecf20Sopenharmony_ci		.rx_buf		= pad->response,
778c2ecf20Sopenharmony_ci		.len		= sendcmdlen,
788c2ecf20Sopenharmony_ci	};
798c2ecf20Sopenharmony_ci	int err;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	err = spi_sync_transfer(pad->spi, &xfers, 1);
828c2ecf20Sopenharmony_ci	if (err) {
838c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
848c2ecf20Sopenharmony_ci			"%s: failed to SPI xfers mode: %d\n",
858c2ecf20Sopenharmony_ci			__func__, err);
868c2ecf20Sopenharmony_ci		return err;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
938c2ecf20Sopenharmony_cistatic void psxpad_control_motor(struct psxpad *pad,
948c2ecf20Sopenharmony_ci				 bool motor1enable, bool motor2enable)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int err;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	pad->motor1enable = motor1enable;
998c2ecf20Sopenharmony_ci	pad->motor2enable = motor2enable;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	memcpy(pad->sendbuf, PSX_CMD_ENTER_CFG, sizeof(PSX_CMD_ENTER_CFG));
1028c2ecf20Sopenharmony_ci	err = psxpad_command(pad, sizeof(PSX_CMD_ENTER_CFG));
1038c2ecf20Sopenharmony_ci	if (err) {
1048c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
1058c2ecf20Sopenharmony_ci			"%s: failed to enter config mode: %d\n",
1068c2ecf20Sopenharmony_ci			__func__, err);
1078c2ecf20Sopenharmony_ci		return;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	memcpy(pad->sendbuf, PSX_CMD_ENABLE_MOTOR,
1118c2ecf20Sopenharmony_ci	       sizeof(PSX_CMD_ENABLE_MOTOR));
1128c2ecf20Sopenharmony_ci	pad->sendbuf[3] = pad->motor1enable ? 0x00 : 0xFF;
1138c2ecf20Sopenharmony_ci	pad->sendbuf[4] = pad->motor2enable ? 0x80 : 0xFF;
1148c2ecf20Sopenharmony_ci	err = psxpad_command(pad, sizeof(PSX_CMD_ENABLE_MOTOR));
1158c2ecf20Sopenharmony_ci	if (err) {
1168c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
1178c2ecf20Sopenharmony_ci			"%s: failed to enable motor mode: %d\n",
1188c2ecf20Sopenharmony_ci			__func__, err);
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	memcpy(pad->sendbuf, PSX_CMD_EXIT_CFG, sizeof(PSX_CMD_EXIT_CFG));
1238c2ecf20Sopenharmony_ci	err = psxpad_command(pad, sizeof(PSX_CMD_EXIT_CFG));
1248c2ecf20Sopenharmony_ci	if (err) {
1258c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
1268c2ecf20Sopenharmony_ci			"%s: failed to exit config mode: %d\n",
1278c2ecf20Sopenharmony_ci			__func__, err);
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void psxpad_set_motor_level(struct psxpad *pad,
1338c2ecf20Sopenharmony_ci				   u8 motor1level, u8 motor2level)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	pad->motor1level = motor1level ? 0xFF : 0x00;
1368c2ecf20Sopenharmony_ci	pad->motor2level = REVERSE_BIT(motor2level);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int psxpad_spi_play_effect(struct input_dev *idev,
1408c2ecf20Sopenharmony_ci				  void *data, struct ff_effect *effect)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct psxpad *pad = input_get_drvdata(idev);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	switch (effect->type) {
1458c2ecf20Sopenharmony_ci	case FF_RUMBLE:
1468c2ecf20Sopenharmony_ci		psxpad_set_motor_level(pad,
1478c2ecf20Sopenharmony_ci			(effect->u.rumble.weak_magnitude >> 8) & 0xFFU,
1488c2ecf20Sopenharmony_ci			(effect->u.rumble.strong_magnitude >> 8) & 0xFFU);
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int psxpad_spi_init_ff(struct psxpad *pad)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int err;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	input_set_capability(pad->idev, EV_FF, FF_RUMBLE);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_play_effect);
1628c2ecf20Sopenharmony_ci	if (err) {
1638c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
1648c2ecf20Sopenharmony_ci			"input_ff_create_memless() failed: %d\n", err);
1658c2ecf20Sopenharmony_ci		return err;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#else	/* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void psxpad_control_motor(struct psxpad *pad,
1748c2ecf20Sopenharmony_ci				 bool motor1enable, bool motor2enable)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic void psxpad_set_motor_level(struct psxpad *pad,
1798c2ecf20Sopenharmony_ci				   u8 motor1level, u8 motor2level)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic inline int psxpad_spi_init_ff(struct psxpad *pad)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci#endif	/* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int psxpad_spi_poll_open(struct input_dev *input)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct psxpad *pad = input_get_drvdata(input);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pad->spi->dev);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void psxpad_spi_poll_close(struct input_dev *input)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct psxpad *pad = input_get_drvdata(input);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pad->spi->dev);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void psxpad_spi_poll(struct input_dev *input)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct psxpad *pad = input_get_drvdata(input);
2088c2ecf20Sopenharmony_ci	u8 b_rsp3, b_rsp4;
2098c2ecf20Sopenharmony_ci	int err;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	psxpad_control_motor(pad, true, true);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	memcpy(pad->sendbuf, PSX_CMD_POLL, sizeof(PSX_CMD_POLL));
2148c2ecf20Sopenharmony_ci	pad->sendbuf[3] = pad->motor1enable ? pad->motor1level : 0x00;
2158c2ecf20Sopenharmony_ci	pad->sendbuf[4] = pad->motor2enable ? pad->motor2level : 0x00;
2168c2ecf20Sopenharmony_ci	err = psxpad_command(pad, sizeof(PSX_CMD_POLL));
2178c2ecf20Sopenharmony_ci	if (err) {
2188c2ecf20Sopenharmony_ci		dev_err(&pad->spi->dev,
2198c2ecf20Sopenharmony_ci			"%s: poll command failed mode: %d\n", __func__, err);
2208c2ecf20Sopenharmony_ci		return;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	switch (pad->response[1]) {
2248c2ecf20Sopenharmony_ci	case 0xCE:	/* 0x73 : analog 1 */
2258c2ecf20Sopenharmony_ci		/* button data is inverted */
2268c2ecf20Sopenharmony_ci		b_rsp3 = ~pad->response[3];
2278c2ecf20Sopenharmony_ci		b_rsp4 = ~pad->response[4];
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_X, REVERSE_BIT(pad->response[7]));
2308c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_Y, REVERSE_BIT(pad->response[8]));
2318c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_RX, REVERSE_BIT(pad->response[5]));
2328c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_RY, REVERSE_BIT(pad->response[6]));
2338c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3));
2348c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1));
2358c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0));
2368c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2));
2378c2ecf20Sopenharmony_ci		input_report_key(input, BTN_X, b_rsp4 & BIT(3));
2388c2ecf20Sopenharmony_ci		input_report_key(input, BTN_A, b_rsp4 & BIT(2));
2398c2ecf20Sopenharmony_ci		input_report_key(input, BTN_B, b_rsp4 & BIT(1));
2408c2ecf20Sopenharmony_ci		input_report_key(input, BTN_Y, b_rsp4 & BIT(0));
2418c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TL, b_rsp4 & BIT(5));
2428c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TR, b_rsp4 & BIT(4));
2438c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TL2, b_rsp4 & BIT(7));
2448c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TR2, b_rsp4 & BIT(6));
2458c2ecf20Sopenharmony_ci		input_report_key(input, BTN_THUMBL, b_rsp3 & BIT(6));
2468c2ecf20Sopenharmony_ci		input_report_key(input, BTN_THUMBR, b_rsp3 & BIT(5));
2478c2ecf20Sopenharmony_ci		input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7));
2488c2ecf20Sopenharmony_ci		input_report_key(input, BTN_START, b_rsp3 & BIT(4));
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	case 0x82:	/* 0x41 : digital */
2528c2ecf20Sopenharmony_ci		/* button data is inverted */
2538c2ecf20Sopenharmony_ci		b_rsp3 = ~pad->response[3];
2548c2ecf20Sopenharmony_ci		b_rsp4 = ~pad->response[4];
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_X, 0x80);
2578c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_Y, 0x80);
2588c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_RX, 0x80);
2598c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_RY, 0x80);
2608c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3));
2618c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1));
2628c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0));
2638c2ecf20Sopenharmony_ci		input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2));
2648c2ecf20Sopenharmony_ci		input_report_key(input, BTN_X, b_rsp4 & BIT(3));
2658c2ecf20Sopenharmony_ci		input_report_key(input, BTN_A, b_rsp4 & BIT(2));
2668c2ecf20Sopenharmony_ci		input_report_key(input, BTN_B, b_rsp4 & BIT(1));
2678c2ecf20Sopenharmony_ci		input_report_key(input, BTN_Y, b_rsp4 & BIT(0));
2688c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TL, b_rsp4 & BIT(5));
2698c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TR, b_rsp4 & BIT(4));
2708c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TL2, b_rsp4 & BIT(7));
2718c2ecf20Sopenharmony_ci		input_report_key(input, BTN_TR2, b_rsp4 & BIT(6));
2728c2ecf20Sopenharmony_ci		input_report_key(input, BTN_THUMBL, false);
2738c2ecf20Sopenharmony_ci		input_report_key(input, BTN_THUMBR, false);
2748c2ecf20Sopenharmony_ci		input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7));
2758c2ecf20Sopenharmony_ci		input_report_key(input, BTN_START, b_rsp3 & BIT(4));
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	input_sync(input);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int psxpad_spi_probe(struct spi_device *spi)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct psxpad *pad;
2858c2ecf20Sopenharmony_ci	struct input_dev *idev;
2868c2ecf20Sopenharmony_ci	int err;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL);
2898c2ecf20Sopenharmony_ci	if (!pad)
2908c2ecf20Sopenharmony_ci		return -ENOMEM;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	idev = devm_input_allocate_device(&spi->dev);
2938c2ecf20Sopenharmony_ci	if (!idev) {
2948c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "failed to allocate input device\n");
2958c2ecf20Sopenharmony_ci		return -ENOMEM;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* input poll device settings */
2998c2ecf20Sopenharmony_ci	pad->idev = idev;
3008c2ecf20Sopenharmony_ci	pad->spi = spi;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* input device settings */
3038c2ecf20Sopenharmony_ci	input_set_drvdata(idev, pad);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	idev->name = "PlayStation 1/2 joypad";
3068c2ecf20Sopenharmony_ci	snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev));
3078c2ecf20Sopenharmony_ci	idev->id.bustype = BUS_SPI;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	idev->open = psxpad_spi_poll_open;
3108c2ecf20Sopenharmony_ci	idev->close = psxpad_spi_poll_close;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* key/value map settings */
3138c2ecf20Sopenharmony_ci	input_set_abs_params(idev, ABS_X, 0, 255, 0, 0);
3148c2ecf20Sopenharmony_ci	input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0);
3158c2ecf20Sopenharmony_ci	input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
3168c2ecf20Sopenharmony_ci	input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
3178c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_DPAD_UP);
3188c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN);
3198c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT);
3208c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT);
3218c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_A);
3228c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_B);
3238c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_X);
3248c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_Y);
3258c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_TL);
3268c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_TR);
3278c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_TL2);
3288c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_TR2);
3298c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_THUMBL);
3308c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_THUMBR);
3318c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_SELECT);
3328c2ecf20Sopenharmony_ci	input_set_capability(idev, EV_KEY, BTN_START);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	err = psxpad_spi_init_ff(pad);
3358c2ecf20Sopenharmony_ci	if (err)
3368c2ecf20Sopenharmony_ci		return err;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* SPI settings */
3398c2ecf20Sopenharmony_ci	spi->mode = SPI_MODE_3;
3408c2ecf20Sopenharmony_ci	spi->bits_per_word = 8;
3418c2ecf20Sopenharmony_ci	/* (PlayStation 1/2 joypad might be possible works 250kHz/500kHz) */
3428c2ecf20Sopenharmony_ci	spi->master->min_speed_hz = 125000;
3438c2ecf20Sopenharmony_ci	spi->master->max_speed_hz = 125000;
3448c2ecf20Sopenharmony_ci	spi_setup(spi);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* pad settings */
3478c2ecf20Sopenharmony_ci	psxpad_set_motor_level(pad, 0, 0);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	err = input_setup_polling(idev, psxpad_spi_poll);
3518c2ecf20Sopenharmony_ci	if (err) {
3528c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "failed to set up polling: %d\n", err);
3538c2ecf20Sopenharmony_ci		return err;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* poll interval is about 60fps */
3578c2ecf20Sopenharmony_ci	input_set_poll_interval(idev, 16);
3588c2ecf20Sopenharmony_ci	input_set_min_poll_interval(idev, 8);
3598c2ecf20Sopenharmony_ci	input_set_max_poll_interval(idev, 32);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* register input poll device */
3628c2ecf20Sopenharmony_ci	err = input_register_device(idev);
3638c2ecf20Sopenharmony_ci	if (err) {
3648c2ecf20Sopenharmony_ci		dev_err(&spi->dev,
3658c2ecf20Sopenharmony_ci			"failed to register input device: %d\n", err);
3668c2ecf20Sopenharmony_ci		return err;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	pm_runtime_enable(&spi->dev);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int __maybe_unused psxpad_spi_suspend(struct device *dev)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
3778c2ecf20Sopenharmony_ci	struct psxpad *pad = spi_get_drvdata(spi);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	psxpad_set_motor_level(pad, 0, 0);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, NULL);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic const struct spi_device_id psxpad_spi_id[] = {
3878c2ecf20Sopenharmony_ci	{ "psxpad-spi", 0 },
3888c2ecf20Sopenharmony_ci	{ }
3898c2ecf20Sopenharmony_ci};
3908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, psxpad_spi_id);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic struct spi_driver psxpad_spi_driver = {
3938c2ecf20Sopenharmony_ci	.driver = {
3948c2ecf20Sopenharmony_ci		.name = "psxpad-spi",
3958c2ecf20Sopenharmony_ci		.pm = &psxpad_spi_pm,
3968c2ecf20Sopenharmony_ci	},
3978c2ecf20Sopenharmony_ci	.id_table = psxpad_spi_id,
3988c2ecf20Sopenharmony_ci	.probe   = psxpad_spi_probe,
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cimodule_spi_driver(psxpad_spi_driver);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tomohiro Yoshidomi <sylph23k@gmail.com>");
4048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PlayStation 1/2 joypads via SPI interface Driver");
4058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
406