Tomohiro Yoshidomi | 8be193c | 2017-05-06 13:00:31 -0700 | [diff] [blame] | 1 | /* |
| 2 | * PlayStation 1/2 joypads via SPI interface Driver |
| 3 | * |
| 4 | * Copyright (C) 2017 Tomohiro Yoshidomi <sylph23k@gmail.com> |
| 5 | * Licensed under the GPL-2 or later. |
| 6 | * |
| 7 | * PlayStation 1/2 joypad's plug (not socket) |
| 8 | * 123 456 789 |
| 9 | * (...|...|...) |
| 10 | * |
| 11 | * 1: DAT -> MISO (pullup with 1k owm to 3.3V) |
| 12 | * 2: CMD -> MOSI |
| 13 | * 3: 9V (for motor, if not use N.C.) |
| 14 | * 4: GND |
| 15 | * 5: 3.3V |
| 16 | * 6: Attention -> CS(SS) |
| 17 | * 7: SCK -> SCK |
| 18 | * 8: N.C. |
| 19 | * 9: ACK -> N.C. |
| 20 | */ |
| 21 | |
| 22 | #include <linux/kernel.h> |
| 23 | #include <linux/device.h> |
| 24 | #include <linux/input.h> |
| 25 | #include <linux/input-polldev.h> |
| 26 | #include <linux/module.h> |
| 27 | #include <linux/spi/spi.h> |
| 28 | #include <linux/types.h> |
| 29 | #include <linux/pm.h> |
| 30 | #include <linux/pm_runtime.h> |
| 31 | |
| 32 | #define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | \ |
| 33 | (((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | \ |
| 34 | (((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7)) |
| 35 | |
| 36 | /* PlayStation 1/2 joypad command and response are LSBFIRST. */ |
| 37 | |
| 38 | /* |
| 39 | * 0x01, 0x42, 0x00, 0x00, 0x00, |
| 40 | * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 41 | * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| 42 | */ |
| 43 | static const u8 PSX_CMD_POLL[] = { |
| 44 | 0x80, 0x42, 0x00, 0x00, 0x00, |
| 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| 47 | }; |
| 48 | /* 0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 */ |
| 49 | static const u8 PSX_CMD_ENTER_CFG[] = { |
| 50 | 0x80, 0xC2, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 |
| 51 | }; |
| 52 | /* 0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A */ |
| 53 | static const u8 PSX_CMD_EXIT_CFG[] = { |
| 54 | 0x80, 0xC2, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A |
| 55 | }; |
| 56 | /* 0x01, 0x4D, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF */ |
| 57 | static const u8 PSX_CMD_ENABLE_MOTOR[] = { |
| 58 | 0x80, 0xB2, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF |
| 59 | }; |
| 60 | |
| 61 | struct psxpad { |
| 62 | struct spi_device *spi; |
| 63 | struct input_polled_dev *pdev; |
| 64 | char phys[0x20]; |
| 65 | bool motor1enable; |
| 66 | bool motor2enable; |
| 67 | u8 motor1level; |
| 68 | u8 motor2level; |
| 69 | u8 sendbuf[0x20] ____cacheline_aligned; |
| 70 | u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned; |
| 71 | }; |
| 72 | |
| 73 | static int psxpad_command(struct psxpad *pad, const u8 sendcmdlen) |
| 74 | { |
| 75 | struct spi_transfer xfers = { |
| 76 | .tx_buf = pad->sendbuf, |
| 77 | .rx_buf = pad->response, |
| 78 | .len = sendcmdlen, |
| 79 | }; |
| 80 | int err; |
| 81 | |
| 82 | err = spi_sync_transfer(pad->spi, &xfers, 1); |
| 83 | if (err) { |
| 84 | dev_err(&pad->spi->dev, |
| 85 | "%s: failed to SPI xfers mode: %d\n", |
| 86 | __func__, err); |
| 87 | return err; |
| 88 | } |
| 89 | |
| 90 | return 0; |
| 91 | } |
| 92 | |
| 93 | #ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF |
| 94 | static void psxpad_control_motor(struct psxpad *pad, |
| 95 | bool motor1enable, bool motor2enable) |
| 96 | { |
| 97 | int err; |
| 98 | |
| 99 | pad->motor1enable = motor1enable; |
| 100 | pad->motor2enable = motor2enable; |
| 101 | |
| 102 | memcpy(pad->sendbuf, PSX_CMD_ENTER_CFG, sizeof(PSX_CMD_ENTER_CFG)); |
| 103 | err = psxpad_command(pad, sizeof(PSX_CMD_ENTER_CFG)); |
| 104 | if (err) { |
| 105 | dev_err(&pad->spi->dev, |
| 106 | "%s: failed to enter config mode: %d\n", |
| 107 | __func__, err); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | memcpy(pad->sendbuf, PSX_CMD_ENABLE_MOTOR, |
| 112 | sizeof(PSX_CMD_ENABLE_MOTOR)); |
| 113 | pad->sendbuf[3] = pad->motor1enable ? 0x00 : 0xFF; |
| 114 | pad->sendbuf[4] = pad->motor2enable ? 0x80 : 0xFF; |
| 115 | err = psxpad_command(pad, sizeof(PSX_CMD_ENABLE_MOTOR)); |
| 116 | if (err) { |
| 117 | dev_err(&pad->spi->dev, |
| 118 | "%s: failed to enable motor mode: %d\n", |
| 119 | __func__, err); |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | memcpy(pad->sendbuf, PSX_CMD_EXIT_CFG, sizeof(PSX_CMD_EXIT_CFG)); |
| 124 | err = psxpad_command(pad, sizeof(PSX_CMD_EXIT_CFG)); |
| 125 | if (err) { |
| 126 | dev_err(&pad->spi->dev, |
| 127 | "%s: failed to exit config mode: %d\n", |
| 128 | __func__, err); |
| 129 | return; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | static void psxpad_set_motor_level(struct psxpad *pad, |
| 134 | u8 motor1level, u8 motor2level) |
| 135 | { |
| 136 | pad->motor1level = motor1level ? 0xFF : 0x00; |
| 137 | pad->motor2level = REVERSE_BIT(motor2level); |
| 138 | } |
| 139 | |
| 140 | static int psxpad_spi_play_effect(struct input_dev *idev, |
| 141 | void *data, struct ff_effect *effect) |
| 142 | { |
| 143 | struct input_polled_dev *pdev = input_get_drvdata(idev); |
| 144 | struct psxpad *pad = pdev->private; |
| 145 | |
| 146 | switch (effect->type) { |
| 147 | case FF_RUMBLE: |
| 148 | psxpad_set_motor_level(pad, |
| 149 | (effect->u.rumble.weak_magnitude >> 8) & 0xFFU, |
| 150 | (effect->u.rumble.strong_magnitude >> 8) & 0xFFU); |
| 151 | break; |
| 152 | } |
| 153 | |
| 154 | return 0; |
| 155 | } |
| 156 | |
| 157 | static int psxpad_spi_init_ff(struct psxpad *pad) |
| 158 | { |
| 159 | int err; |
| 160 | |
| 161 | input_set_capability(pad->pdev->input, EV_FF, FF_RUMBLE); |
| 162 | |
| 163 | err = input_ff_create_memless(pad->pdev->input, NULL, |
| 164 | psxpad_spi_play_effect); |
| 165 | if (err) { |
| 166 | dev_err(&pad->spi->dev, |
| 167 | "input_ff_create_memless() failed: %d\n", err); |
| 168 | return err; |
| 169 | } |
| 170 | |
| 171 | return 0; |
| 172 | } |
| 173 | |
| 174 | #else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */ |
| 175 | |
| 176 | static void psxpad_control_motor(struct psxpad *pad, |
| 177 | bool motor1enable, bool motor2enable) |
| 178 | { |
| 179 | } |
| 180 | |
| 181 | static void psxpad_set_motor_level(struct psxpad *pad, |
| 182 | u8 motor1level, u8 motor2level) |
| 183 | { |
| 184 | } |
| 185 | |
| 186 | static inline int psxpad_spi_init_ff(struct psxpad *pad) |
| 187 | { |
| 188 | return 0; |
| 189 | } |
| 190 | #endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */ |
| 191 | |
| 192 | static void psxpad_spi_poll_open(struct input_polled_dev *pdev) |
| 193 | { |
| 194 | struct psxpad *pad = pdev->private; |
| 195 | |
| 196 | pm_runtime_get_sync(&pad->spi->dev); |
| 197 | } |
| 198 | |
| 199 | static void psxpad_spi_poll_close(struct input_polled_dev *pdev) |
| 200 | { |
| 201 | struct psxpad *pad = pdev->private; |
| 202 | |
| 203 | pm_runtime_put_sync(&pad->spi->dev); |
| 204 | } |
| 205 | |
| 206 | static void psxpad_spi_poll(struct input_polled_dev *pdev) |
| 207 | { |
| 208 | struct psxpad *pad = pdev->private; |
| 209 | struct input_dev *input = pdev->input; |
| 210 | u8 b_rsp3, b_rsp4; |
| 211 | int err; |
| 212 | |
| 213 | psxpad_control_motor(pad, true, true); |
| 214 | |
| 215 | memcpy(pad->sendbuf, PSX_CMD_POLL, sizeof(PSX_CMD_POLL)); |
| 216 | pad->sendbuf[3] = pad->motor1enable ? pad->motor1level : 0x00; |
| 217 | pad->sendbuf[4] = pad->motor2enable ? pad->motor2level : 0x00; |
| 218 | err = psxpad_command(pad, sizeof(PSX_CMD_POLL)); |
| 219 | if (err) { |
| 220 | dev_err(&pad->spi->dev, |
| 221 | "%s: poll command failed mode: %d\n", __func__, err); |
| 222 | return; |
| 223 | } |
| 224 | |
| 225 | switch (pad->response[1]) { |
| 226 | case 0xCE: /* 0x73 : analog 1 */ |
| 227 | /* button data is inverted */ |
| 228 | b_rsp3 = ~pad->response[3]; |
| 229 | b_rsp4 = ~pad->response[4]; |
| 230 | |
| 231 | input_report_abs(input, ABS_X, REVERSE_BIT(pad->response[7])); |
| 232 | input_report_abs(input, ABS_Y, REVERSE_BIT(pad->response[8])); |
| 233 | input_report_abs(input, ABS_RX, REVERSE_BIT(pad->response[5])); |
| 234 | input_report_abs(input, ABS_RY, REVERSE_BIT(pad->response[6])); |
| 235 | input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3)); |
| 236 | input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1)); |
| 237 | input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0)); |
| 238 | input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2)); |
| 239 | input_report_key(input, BTN_X, b_rsp4 & BIT(3)); |
| 240 | input_report_key(input, BTN_A, b_rsp4 & BIT(2)); |
| 241 | input_report_key(input, BTN_B, b_rsp4 & BIT(1)); |
| 242 | input_report_key(input, BTN_Y, b_rsp4 & BIT(0)); |
| 243 | input_report_key(input, BTN_TL, b_rsp4 & BIT(5)); |
| 244 | input_report_key(input, BTN_TR, b_rsp4 & BIT(4)); |
| 245 | input_report_key(input, BTN_TL2, b_rsp4 & BIT(7)); |
| 246 | input_report_key(input, BTN_TR2, b_rsp4 & BIT(6)); |
| 247 | input_report_key(input, BTN_THUMBL, b_rsp3 & BIT(6)); |
| 248 | input_report_key(input, BTN_THUMBR, b_rsp3 & BIT(5)); |
| 249 | input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7)); |
| 250 | input_report_key(input, BTN_START, b_rsp3 & BIT(4)); |
| 251 | break; |
| 252 | |
| 253 | case 0x82: /* 0x41 : digital */ |
| 254 | /* button data is inverted */ |
| 255 | b_rsp3 = ~pad->response[3]; |
| 256 | b_rsp4 = ~pad->response[4]; |
| 257 | |
| 258 | input_report_abs(input, ABS_X, 0x80); |
| 259 | input_report_abs(input, ABS_Y, 0x80); |
| 260 | input_report_abs(input, ABS_RX, 0x80); |
| 261 | input_report_abs(input, ABS_RY, 0x80); |
| 262 | input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3)); |
| 263 | input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1)); |
| 264 | input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0)); |
| 265 | input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2)); |
| 266 | input_report_key(input, BTN_X, b_rsp4 & BIT(3)); |
| 267 | input_report_key(input, BTN_A, b_rsp4 & BIT(2)); |
| 268 | input_report_key(input, BTN_B, b_rsp4 & BIT(1)); |
| 269 | input_report_key(input, BTN_Y, b_rsp4 & BIT(0)); |
| 270 | input_report_key(input, BTN_TL, b_rsp4 & BIT(5)); |
| 271 | input_report_key(input, BTN_TR, b_rsp4 & BIT(4)); |
| 272 | input_report_key(input, BTN_TL2, b_rsp4 & BIT(7)); |
| 273 | input_report_key(input, BTN_TR2, b_rsp4 & BIT(6)); |
| 274 | input_report_key(input, BTN_THUMBL, false); |
| 275 | input_report_key(input, BTN_THUMBR, false); |
| 276 | input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7)); |
| 277 | input_report_key(input, BTN_START, b_rsp3 & BIT(4)); |
| 278 | break; |
| 279 | } |
| 280 | |
| 281 | input_sync(input); |
| 282 | } |
| 283 | |
| 284 | static int psxpad_spi_probe(struct spi_device *spi) |
| 285 | { |
| 286 | struct psxpad *pad; |
| 287 | struct input_polled_dev *pdev; |
| 288 | struct input_dev *idev; |
| 289 | int err; |
| 290 | |
| 291 | pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL); |
| 292 | if (!pad) |
| 293 | return -ENOMEM; |
| 294 | |
| 295 | pdev = input_allocate_polled_device(); |
| 296 | if (!pdev) { |
| 297 | dev_err(&spi->dev, "failed to allocate input device\n"); |
| 298 | return -ENOMEM; |
| 299 | } |
| 300 | |
| 301 | /* input poll device settings */ |
| 302 | pad->pdev = pdev; |
| 303 | pad->spi = spi; |
| 304 | |
| 305 | pdev->private = pad; |
| 306 | pdev->open = psxpad_spi_poll_open; |
| 307 | pdev->close = psxpad_spi_poll_close; |
| 308 | pdev->poll = psxpad_spi_poll; |
| 309 | /* poll interval is about 60fps */ |
| 310 | pdev->poll_interval = 16; |
| 311 | pdev->poll_interval_min = 8; |
| 312 | pdev->poll_interval_max = 32; |
| 313 | |
| 314 | /* input device settings */ |
| 315 | idev = pdev->input; |
| 316 | idev->name = "PlayStation 1/2 joypad"; |
| 317 | snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev)); |
| 318 | idev->id.bustype = BUS_SPI; |
| 319 | |
| 320 | /* key/value map settings */ |
| 321 | input_set_abs_params(idev, ABS_X, 0, 255, 0, 0); |
| 322 | input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0); |
| 323 | input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0); |
| 324 | input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0); |
| 325 | input_set_capability(idev, EV_KEY, BTN_DPAD_UP); |
| 326 | input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN); |
| 327 | input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT); |
| 328 | input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT); |
| 329 | input_set_capability(idev, EV_KEY, BTN_A); |
| 330 | input_set_capability(idev, EV_KEY, BTN_B); |
| 331 | input_set_capability(idev, EV_KEY, BTN_X); |
| 332 | input_set_capability(idev, EV_KEY, BTN_Y); |
| 333 | input_set_capability(idev, EV_KEY, BTN_TL); |
| 334 | input_set_capability(idev, EV_KEY, BTN_TR); |
| 335 | input_set_capability(idev, EV_KEY, BTN_TL2); |
| 336 | input_set_capability(idev, EV_KEY, BTN_TR2); |
| 337 | input_set_capability(idev, EV_KEY, BTN_THUMBL); |
| 338 | input_set_capability(idev, EV_KEY, BTN_THUMBR); |
| 339 | input_set_capability(idev, EV_KEY, BTN_SELECT); |
| 340 | input_set_capability(idev, EV_KEY, BTN_START); |
| 341 | |
| 342 | err = psxpad_spi_init_ff(pad); |
| 343 | if (err) |
| 344 | return err; |
| 345 | |
| 346 | /* SPI settings */ |
| 347 | spi->mode = SPI_MODE_3; |
| 348 | spi->bits_per_word = 8; |
| 349 | /* (PlayStation 1/2 joypad might be possible works 250kHz/500kHz) */ |
| 350 | spi->master->min_speed_hz = 125000; |
| 351 | spi->master->max_speed_hz = 125000; |
| 352 | spi_setup(spi); |
| 353 | |
| 354 | /* pad settings */ |
| 355 | psxpad_set_motor_level(pad, 0, 0); |
| 356 | |
| 357 | /* register input poll device */ |
| 358 | err = input_register_polled_device(pdev); |
| 359 | if (err) { |
| 360 | dev_err(&spi->dev, |
| 361 | "failed to register input poll device: %d\n", err); |
| 362 | return err; |
| 363 | } |
| 364 | |
| 365 | pm_runtime_enable(&spi->dev); |
| 366 | |
| 367 | return 0; |
| 368 | } |
| 369 | |
| 370 | static int __maybe_unused psxpad_spi_suspend(struct device *dev) |
| 371 | { |
| 372 | struct spi_device *spi = to_spi_device(dev); |
| 373 | struct psxpad *pad = spi_get_drvdata(spi); |
| 374 | |
| 375 | psxpad_set_motor_level(pad, 0, 0); |
| 376 | |
| 377 | return 0; |
| 378 | } |
| 379 | |
| 380 | static SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, NULL); |
| 381 | |
| 382 | static const struct spi_device_id psxpad_spi_id[] = { |
| 383 | { "psxpad-spi", 0 }, |
| 384 | { } |
| 385 | }; |
| 386 | MODULE_DEVICE_TABLE(spi, psxpad_spi_id); |
| 387 | |
| 388 | static struct spi_driver psxpad_spi_driver = { |
| 389 | .driver = { |
| 390 | .name = "psxpad-spi", |
| 391 | .pm = &psxpad_spi_pm, |
| 392 | }, |
| 393 | .id_table = psxpad_spi_id, |
| 394 | .probe = psxpad_spi_probe, |
| 395 | }; |
| 396 | |
| 397 | module_spi_driver(psxpad_spi_driver); |
| 398 | |
| 399 | MODULE_AUTHOR("Tomohiro Yoshidomi <sylph23k@gmail.com>"); |
| 400 | MODULE_DESCRIPTION("PlayStation 1/2 joypads via SPI interface Driver"); |
| 401 | MODULE_LICENSE("GPL"); |