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