root/drivers/input/joystick/psxpad-spi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. psxpad_command
  2. psxpad_control_motor
  3. psxpad_set_motor_level
  4. psxpad_spi_play_effect
  5. psxpad_spi_init_ff
  6. psxpad_control_motor
  7. psxpad_set_motor_level
  8. psxpad_spi_init_ff
  9. psxpad_spi_poll_open
  10. psxpad_spi_poll_close
  11. psxpad_spi_poll
  12. psxpad_spi_probe
  13. psxpad_spi_suspend

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * PlayStation 1/2 joypads via SPI interface Driver
   4  *
   5  * Copyright (C) 2017 Tomohiro Yoshidomi <sylph23k@gmail.com>
   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 = devm_input_allocate_polled_device(&spi->dev);
 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");

/* [<][>][^][v][top][bottom][index][help] */