/*----------------------------------------------------------------------------*/ /* * SARADC dual-joystick gamepad driver */ /*----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*----------------------------------------------------------------------------*/ #define DRV_NAME "retrogame_joypad" /*----------------------------------------------------------------------------*/ #define ADC_MAX_VOLTAGE 1800 #define ADC_DATA_TUNING(x, p) ((x * p) / 100) #define ADC_TUNING_DEFAULT 180 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) struct bt_adc { /* report value (mV) */ int value; /* report type */ int report_type; /* input device init value (mV) */ int max, min; /* calibrated adc value */ int cal; /* adc scale value */ int scale; /* invert report */ bool invert; /* amux channel */ int amux_ch; /* adc data tuning value([percent), p = positive, n = negative */ int tuning_p, tuning_n; /* if use dedicated adc channel, it'll be saved here*/ struct iio_channel *iio_ch; }; struct analog_mux { /* IIO ADC Channel : amux connect channel */ struct iio_channel *iio_ch; /* analog mux select(a,b) gpio */ int sel_a_gpio, sel_b_gpio; /* analog mux enable gpio */ int en_gpio; }; struct bt_gpio { /* GPIO Request label */ const char *label; /* GPIO Number */ int num; /* report type */ int report_type; /* report linux code */ int linux_code; /* prev button value */ bool old_value; /* button press level */ bool active_level; }; struct joypad { struct device *dev; int poll_interval; /* report enable/disable */ bool enable; /* analog mux & joystick control */ struct analog_mux *amux; /* analog mux max count */ int amux_count; bool amux_dedicated; /* analog button */ struct bt_adc *adcs; /* report reference point */ bool invert_absx; bool invert_absy; bool invert_absrx; bool invert_absry; /* report interval (ms) */ int bt_gpio_count; struct bt_gpio *gpios; /* button auto repeat */ int auto_repeat; /* report threshold (mV) */ int bt_adc_fuzz, bt_adc_flat; /* adc read value scale */ int bt_adc_scale; /* joystick deadzone control */ int bt_adc_deadzone; struct mutex lock; /* amux debug channel */ int debug_ch; /* pwm device for rumble*/ struct input_dev *input; struct pwm_device *pwm; struct work_struct play_work; u16 level; u16 boost_weak; u16 boost_strong; }; static int pwm_vibrator_start(struct joypad *joypad) { struct device *pdev = joypad->input->dev.parent; struct pwm_state state; int err; pwm_get_state(joypad->pwm, &state); pwm_set_relative_duty_cycle(&state, joypad->level, 0xffff); state.enabled = true; err = pwm_apply_state(joypad->pwm, &state); if (err) { dev_err(pdev, "failed to apply pwm state: %d", err); return err; } return 0; } static void pwm_vibrator_stop(struct joypad *joypad) { pwm_disable(joypad->pwm); } static void pwm_vibrator_play_work(struct work_struct *work) { struct joypad *joypad = container_of(work, struct joypad, play_work); if (joypad->level) pwm_vibrator_start(joypad); else pwm_vibrator_stop(joypad); } /*----------------------------------------------------------------------------*/ // // set to the value in the boot.ini file. (if exist) // /*----------------------------------------------------------------------------*/ static unsigned int g_button_adc_fuzz = 0; static unsigned int g_button_adc_flat = 0; static unsigned int g_button_adc_scale = 0; static unsigned int g_button_adc_deadzone = 0; static int button_adc_fuzz(char *str) { if (!str) return -EINVAL; g_button_adc_fuzz = simple_strtoul(str, NULL, 10); return 0; } __setup("button-adc-fuzz=", button_adc_fuzz); static int button_adc_flat(char *str) { if (!str) return -EINVAL; g_button_adc_flat = simple_strtoul(str, NULL, 10); return 0; } __setup("button-adc-flat=", button_adc_flat); static int button_adc_scale(char *str) { if (!str) return -EINVAL; g_button_adc_scale = simple_strtoul(str, NULL, 10); return 0; } __setup("button-adc-scale=", button_adc_scale); static int button_adc_deadzone(char *str) { if (!str) return -EINVAL; g_button_adc_deadzone = simple_strtoul(str, NULL, 10); return 0; } __setup("button-adc-deadzone=", button_adc_deadzone); /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ static int joypad_amux_select(struct analog_mux *amux, int channel) { /* select mux channel */ gpio_set_value(amux->en_gpio, 0); switch(channel) { case 0: /* EVENT (ABS_RY) */ gpio_set_value(amux->sel_a_gpio, 0); gpio_set_value(amux->sel_b_gpio, 0); break; case 1: /* EVENT (ABS_RX) */ gpio_set_value(amux->sel_a_gpio, 0); gpio_set_value(amux->sel_b_gpio, 1); break; case 2: /* EVENT (ABS_Y) */ gpio_set_value(amux->sel_a_gpio, 1); gpio_set_value(amux->sel_b_gpio, 0); break; case 3: /* EVENT (ABS_X) */ gpio_set_value(amux->sel_a_gpio, 1); gpio_set_value(amux->sel_b_gpio, 1); break; default: /* amux disanle */ gpio_set_value(amux->en_gpio, 1); return -1; } /* mux swtiching speed : 35ns(on) / 9ns(off) */ usleep_range(10, 20); return 0; } /*----------------------------------------------------------------------------*/ static int joypad_adc_read(struct joypad *joypad, struct bt_adc *adc) { int value; if(joypad->amux_dedicated){ iio_read_channel_raw(adc->iio_ch, &value); } else{ struct analog_mux *amux = joypad->amux; if (joypad_amux_select(amux, adc->amux_ch)) return 0; iio_read_channel_raw(amux->iio_ch, &value); } value *= adc->scale; return value; } /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/poll_interval [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_poll_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); mutex_lock(&joypad->lock); joypad->poll_interval = simple_strtoul(buf, NULL, 10); mutex_unlock(&joypad->lock); return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_poll_interval(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->poll_interval); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(poll_interval, S_IWUSR | S_IRUGO, joypad_show_poll_interval, joypad_store_poll_interval); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/adc_fuzz [r] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_adc_fuzz(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->bt_adc_fuzz); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(adc_fuzz, S_IWUSR | S_IRUGO, joypad_show_adc_fuzz, NULL); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/adc_flat [r] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_adc_flat(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->bt_adc_flat); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(adc_flat, S_IWUSR | S_IRUGO, joypad_show_adc_flat, NULL); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/enable [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); mutex_lock(&joypad->lock); joypad->enable = simple_strtoul(buf, NULL, 10); mutex_unlock(&joypad->lock); return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->enable); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, joypad_show_enable, joypad_store_enable); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/adc_cal [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_adc_cal(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); bool calibration; calibration = simple_strtoul(buf, NULL, 10); if (calibration) { int nbtn; mutex_lock(&joypad->lock); for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; adc->value = joypad_adc_read(joypad, adc); if (!adc->value) { dev_err(joypad->dev, "%s : saradc channels[%d]!\n", __func__, nbtn); continue; } adc->cal = adc->value; } mutex_unlock(&joypad->lock); } return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_adc_cal(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); int nbtn; ssize_t pos; for (nbtn = 0, pos = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; pos += sprintf(&buf[pos], "adc[%d]->cal = %d\n", nbtn, adc->cal); } pos += sprintf(&buf[pos], "adc scale = %d\n", joypad->bt_adc_scale); return pos; } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(adc_cal, S_IWUSR | S_IRUGO, joypad_show_adc_cal, joypad_store_adc_cal); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/retrogame_joypad/amux_debug [rw] * * echo [debug channel] > amux_debug * cat amux_debug : debug channel mux set & adc read */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_amux_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); joypad->debug_ch = simple_strtoul(buf, NULL, 10); /* if error than default setting(debug_ch = 0) */ if (joypad->debug_ch > joypad->amux_count) joypad->debug_ch = 0; return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_amux_debug(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); ssize_t pos; int value; mutex_lock(&joypad->lock); /* disable poll driver */ if (joypad->enable) joypad->enable = false; if(joypad->amux_dedicated){ if (iio_read_channel_processed(joypad->adcs[joypad->debug_ch].iio_ch, &value)) goto err_out; } else{ struct analog_mux *amux = joypad->amux; if (joypad_amux_select(amux, joypad->debug_ch)) goto err_out; if (iio_read_channel_processed(amux->iio_ch, &value)) goto err_out; } pos = sprintf(buf, "amux ch[%d], adc scale = %d, adc value = %d\n", joypad->debug_ch, joypad->bt_adc_scale, value * joypad->bt_adc_scale); goto out; err_out: pos = sprintf(buf, "error : amux setup & adc read!\n"); out: mutex_unlock(&joypad->lock); return pos; } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(amux_debug, S_IWUSR | S_IRUGO, joypad_show_amux_debug, joypad_store_amux_debug); /*----------------------------------------------------------------------------*/ #define __HDMI__ #ifdef __HDMI__ /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/singleadc-joypad/hdst [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_hdst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_hdst(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); //int stcon; ssize_t pos=0; if(gpio_get_value(54)==1) { pos += sprintf(&buf[pos], "disconnected\n"); }else{ pos += sprintf(&buf[pos], "connected\n"); } return pos; } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(hdst, S_IWUSR | S_IRUGO, joypad_show_hdst, joypad_store_hdst); #endif /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/odroidgo2_joypad/rumble_period [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_period(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); mutex_lock(&joypad->lock); pwm_set_period(joypad->pwm, simple_strtoul(buf, NULL, 21)); mutex_unlock(&joypad->lock); return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_period(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", pwm_get_period(joypad->pwm)); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(rumble_period, S_IWUSR | S_IRUGO, joypad_show_period, joypad_store_period); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/odroidgo2_joypad/rumble_boost_strong [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_boost_strong(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); mutex_lock(&joypad->lock); joypad->boost_strong = simple_strtoul(buf, NULL, 10); mutex_unlock(&joypad->lock); return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_boost_strong(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->boost_strong); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(rumble_boost_strong, S_IWUSR | S_IRUGO, joypad_show_boost_strong, joypad_store_boost_strong); /*----------------------------------------------------------------------------*/ /* * ATTRIBUTES: * * /sys/devices/platform/odroidgo2_joypad/rumble_boost_weak [rw] */ /*----------------------------------------------------------------------------*/ static ssize_t joypad_store_boost_weak(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); mutex_lock(&joypad->lock); joypad->boost_weak = simple_strtoul(buf, NULL, 10); mutex_unlock(&joypad->lock); return count; } /*----------------------------------------------------------------------------*/ static ssize_t joypad_show_boost_weak(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); return sprintf(buf, "%d\n", joypad->boost_weak); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(rumble_boost_weak, S_IWUSR | S_IRUGO, joypad_show_boost_weak, joypad_store_boost_weak); #ifdef __HDMI__ static struct attribute *joypad_attrs[] = { &dev_attr_poll_interval.attr, &dev_attr_adc_fuzz.attr, &dev_attr_adc_flat.attr, &dev_attr_enable.attr, &dev_attr_adc_cal.attr, &dev_attr_amux_debug.attr, &dev_attr_hdst.attr, &dev_attr_rumble_period.attr, &dev_attr_rumble_boost_strong.attr, &dev_attr_rumble_boost_weak.attr, NULL, }; #else /*----------------------------------------------------------------------------*/ static struct attribute *joypad_attrs[] = { &dev_attr_poll_interval.attr, &dev_attr_adc_fuzz.attr, &dev_attr_adc_flat.attr, &dev_attr_enable.attr, &dev_attr_adc_cal.attr, &dev_attr_amux_debug.attr, &dev_attr_rumble_period.attr, &dev_attr_rumble_boost_strong.attr, &dev_attr_rumble_boost_weak.attr, NULL, }; #endif static struct attribute_group joypad_attr_group = { .attrs = joypad_attrs, }; /*----------------------------------------------------------------------------*/ static void joypad_gpio_check(struct input_polled_dev *poll_dev) { struct joypad *joypad = poll_dev->private; int nbtn, value; for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { struct bt_gpio *gpio = &joypad->gpios[nbtn]; if (gpio_get_value_cansleep(gpio->num) < 0) { dev_err(joypad->dev, "failed to get gpio state\n"); continue; } value = gpio_get_value(gpio->num); if (value != gpio->old_value) { int keyval = (value == gpio->active_level) ? 1 : 0; dev_info(joypad->dev, "key status update, code=%d, val=%d", gpio->linux_code, keyval); input_event(poll_dev->input, gpio->report_type, gpio->linux_code, keyval); gpio->old_value = value; } } input_sync(poll_dev->input); } /*----------------------------------------------------------------------------*/ static void joypad_adc_check(struct input_polled_dev *poll_dev) { struct joypad *joypad = poll_dev->private; int nbtn; for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; int oldVal = adc->value; adc->value = joypad_adc_read(joypad, adc); if (!adc->value) { //dev_err(joypad->dev, "%s : saradc channels[%d]! adc->value : %d\n",__func__, nbtn, adc->value); continue; } adc->value = adc->value - adc->cal; /* Joystick Deadzone check */ if (joypad->bt_adc_deadzone) { if (abs(adc->value) < joypad->bt_adc_deadzone) adc->value = 0; } /* adc data tuning */ if (adc->tuning_n && adc->value < 0) adc->value = ADC_DATA_TUNING(adc->value, adc->tuning_n); if (adc->tuning_p && adc->value > 0) adc->value = ADC_DATA_TUNING(adc->value, adc->tuning_p); adc->value = adc->value > adc->max ? adc->max : adc->value; adc->value = adc->value < adc->min ? adc->min : adc->value; if(adc->value != oldVal) { //dev_info(joypad->dev, "saradc channels[%d]! adc->value : %d, oldVal : %d", nbtn, adc->value, oldVal); } input_report_abs(poll_dev->input, adc->report_type, adc->invert ? adc->value * (-1) : adc->value); } input_sync(poll_dev->input); } /*----------------------------------------------------------------------------*/ static void joypad_poll(struct input_polled_dev *poll_dev) { struct joypad *joypad = poll_dev->private; if (joypad->enable) { joypad_adc_check(poll_dev); joypad_gpio_check(poll_dev); } if (poll_dev->poll_interval != joypad->poll_interval) { mutex_lock(&joypad->lock); poll_dev->poll_interval = joypad->poll_interval; mutex_unlock(&joypad->lock); } } /*----------------------------------------------------------------------------*/ static void joypad_open(struct input_polled_dev *poll_dev) { struct joypad *joypad = poll_dev->private; int nbtn; for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { struct bt_gpio *gpio = &joypad->gpios[nbtn]; gpio->old_value = gpio->active_level ? 0 : 1; } for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; adc->value = joypad_adc_read(joypad, adc); if (!adc->value) { dev_err(joypad->dev, "%s : saradc channels[%d]!\n", __func__, nbtn); continue; } adc->cal = adc->value; dev_info(joypad->dev, "%s : adc[%d] adc->cal = %d\n", __func__, nbtn, adc->cal); } /* buttons status sync */ joypad_adc_check(poll_dev); joypad_gpio_check(poll_dev); /* button report enable */ mutex_lock(&joypad->lock); joypad->enable = true; mutex_unlock(&joypad->lock); dev_info(joypad->dev, "%s : opened\n", __func__); } /*----------------------------------------------------------------------------*/ static void joypad_close(struct input_polled_dev *poll_dev) { struct joypad *joypad = poll_dev->private; /* button report disable */ mutex_lock(&joypad->lock); joypad->enable = false; mutex_unlock(&joypad->lock); if(&joypad->play_work) cancel_work_sync(&joypad->play_work); pwm_vibrator_stop(joypad); dev_info(joypad->dev, "%s : closed\n", __func__); } /*----------------------------------------------------------------------------*/ static int joypad_amux_setup(struct device *dev, struct joypad *joypad) { struct analog_mux *amux; enum iio_chan_type type; enum of_gpio_flags flags; int ret; if(joypad->amux_dedicated) return 0; /* analog mux control struct init */ joypad->amux = devm_kzalloc(dev, sizeof(struct analog_mux), GFP_KERNEL); if (!joypad->amux) { dev_err(dev, "%s amux devm_kzmalloc error!", __func__); return -ENOMEM; } amux = joypad->amux; amux->iio_ch = devm_iio_channel_get(dev, "amux_adc"); if (IS_ERR(amux->iio_ch)) { dev_err(dev, "iio channel get error\n"); return -EINVAL; } if (!amux->iio_ch->indio_dev) return -ENXIO; if (iio_get_channel_type(amux->iio_ch, &type)) return -EINVAL; if (type != IIO_VOLTAGE) { dev_err(dev, "Incompatible channel type %d\n", type); return -EINVAL; } amux->sel_a_gpio = of_get_named_gpio_flags(dev->of_node, "amux-a-gpios", 0, &flags); if (gpio_is_valid(amux->sel_a_gpio)) { ret = devm_gpio_request(dev, amux->sel_a_gpio, "amux-sel-a"); if (ret < 0) { dev_err(dev, "%s : failed to request amux-sel-a %d\n", __func__, amux->sel_a_gpio); goto err_out; } ret = gpio_direction_output(amux->sel_a_gpio, 0); if (ret < 0) goto err_out; } amux->sel_b_gpio = of_get_named_gpio_flags(dev->of_node, "amux-b-gpios", 0, &flags); if (gpio_is_valid(amux->sel_b_gpio)) { ret = devm_gpio_request(dev, amux->sel_b_gpio, "amux-sel-b"); if (ret < 0) { dev_err(dev, "%s : failed to request amux-sel-b %d\n", __func__, amux->sel_b_gpio); goto err_out; } ret = gpio_direction_output(amux->sel_b_gpio, 0); if (ret < 0) goto err_out; } amux->en_gpio = of_get_named_gpio_flags(dev->of_node, "amux-en-gpios", 0, &flags); if (gpio_is_valid(amux->en_gpio)) { ret = devm_gpio_request(dev, amux->en_gpio, "amux-en"); if (ret < 0) { dev_err(dev, "%s : failed to request amux-en %d\n", __func__, amux->en_gpio); goto err_out; } ret = gpio_direction_output(amux->en_gpio, 0); if (ret < 0) goto err_out; } return 0; err_out: return ret; } /*----------------------------------------------------------------------------*/ static int joypad_adc_setup(struct device *dev, struct joypad *joypad) { int nbtn; /* adc button struct init */ joypad->adcs = devm_kzalloc(dev, joypad->amux_count * sizeof(struct bt_adc), GFP_KERNEL); if (!joypad->adcs) { dev_err(dev, "%s devm_kzmalloc error!", __func__); return -ENOMEM; } enum iio_chan_type type; struct iio_channel* channels; if(joypad->amux_dedicated){ channels = devm_iio_channel_get_all(dev); if (IS_ERR(channels)) { dev_err(dev, "iio channel get error\n"); return -EINVAL; } } for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; if(joypad->amux_dedicated){ struct iio_channel* ch = &channels[nbtn]; adc->iio_ch = ch; if (IS_ERR(adc->iio_ch)) { dev_err(dev, "[ch:%d] iio channel get error\n", nbtn); return -EINVAL; } if (!adc->iio_ch->indio_dev) return -ENXIO; if (iio_get_channel_type(adc->iio_ch, &type)) return -EINVAL; if (type != IIO_VOLTAGE) { dev_err(dev, "[ch:%d] Incompatible channel type %d\n", nbtn, type); return -EINVAL; } } adc->scale = joypad->bt_adc_scale; adc->max = (ADC_MAX_VOLTAGE / 2); adc->min = (ADC_MAX_VOLTAGE / 2) * (-1); if (adc->scale) { adc->max *= adc->scale; adc->min *= adc->scale; } adc->amux_ch = nbtn; adc->invert = false; switch (nbtn) { case 0: if (joypad->invert_absry) adc->invert = true; adc->report_type = ABS_RY; if (device_property_read_u32(dev, "abs_ry-p-tuning", &adc->tuning_p)) adc->tuning_p = ADC_TUNING_DEFAULT; if (device_property_read_u32(dev, "abs_ry-n-tuning", &adc->tuning_n)) adc->tuning_n = ADC_TUNING_DEFAULT; break; case 1: if (joypad->invert_absrx) adc->invert = true; adc->report_type = ABS_RX; if (device_property_read_u32(dev, "abs_rx-p-tuning", &adc->tuning_p)) adc->tuning_p = ADC_TUNING_DEFAULT; if (device_property_read_u32(dev, "abs_rx-n-tuning", &adc->tuning_n)) adc->tuning_n = ADC_TUNING_DEFAULT; break; case 2: if (joypad->invert_absy) adc->invert = true; adc->report_type = ABS_Y; if (device_property_read_u32(dev, "abs_y-p-tuning", &adc->tuning_p)) adc->tuning_p = ADC_TUNING_DEFAULT; if (device_property_read_u32(dev, "abs_y-n-tuning", &adc->tuning_n)) adc->tuning_n = ADC_TUNING_DEFAULT; break; case 3: if (joypad->invert_absx) adc->invert = true; adc->report_type = ABS_X; if (device_property_read_u32(dev, "abs_x-p-tuning", &adc->tuning_p)) adc->tuning_p = ADC_TUNING_DEFAULT; if (device_property_read_u32(dev, "abs_x-n-tuning", &adc->tuning_n)) adc->tuning_n = ADC_TUNING_DEFAULT; break; default : dev_err(dev, "%s amux count(%d) error!", __func__, nbtn); return -EINVAL; } } return 0; } /*----------------------------------------------------------------------------*/ static int joypad_gpio_setup(struct device *dev, struct joypad *joypad) { struct device_node *node, *pp; int nbtn; node = dev->of_node; if (!node) return -ENODEV; joypad->gpios = devm_kzalloc(dev, joypad->bt_gpio_count * sizeof(struct bt_gpio), GFP_KERNEL); if (!joypad->gpios) { dev_err(dev, "%s devm_kzmalloc error!", __func__); return -ENOMEM; } nbtn = 0; for_each_child_of_node(node, pp) { enum of_gpio_flags flags; struct bt_gpio *gpio = &joypad->gpios[nbtn++]; int error; gpio->num = of_get_gpio_flags(pp, 0, &flags); if (gpio->num < 0) { error = gpio->num; dev_err(dev, "Failed to get gpio flags, error: %d\n", error); return error; } /* gpio active level(key press level) */ gpio->active_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; gpio->label = of_get_property(pp, "label", NULL); if (gpio_is_valid(gpio->num)) { error = devm_gpio_request_one(dev, gpio->num, GPIOF_IN, gpio->label); if (error < 0) { dev_err(dev, "Failed to request GPIO %d, error %d\n", gpio->num, error); return error; } } if (of_property_read_u32(pp, "linux,code", &gpio->linux_code)) { dev_err(dev, "Button without keycode: 0x%x\n", gpio->num); return -EINVAL; } if (of_property_read_u32(pp, "linux,input-type", &gpio->report_type)) gpio->report_type = EV_KEY; } if (nbtn == 0) return -EINVAL; return 0; } static int rumble_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct joypad *joypad = data; u32 boosted_level; if (effect->type != FF_RUMBLE) return 0; if (effect->u.rumble.strong_magnitude) boosted_level = effect->u.rumble.strong_magnitude + joypad->boost_strong; else boosted_level = effect->u.rumble.weak_magnitude + joypad->boost_weak; joypad->level = (u16)CLAMP(boosted_level, 0, 0xffff); dev_info(joypad->dev,"joypad->level = %d", joypad->level); schedule_work(&joypad->play_work); return 0; } /*----------------------------------------------------------------------------*/ static int joypad_rumble_setup(struct device *dev, struct joypad *joypad) { int err; struct pwm_state state; joypad->pwm = devm_pwm_get(dev, "enable"); if (IS_ERR(joypad->pwm)) { dev_err(dev, "rumble get error\n"); return -EINVAL; } INIT_WORK(&joypad->play_work, pwm_vibrator_play_work); /* Sync up PWM state and ensure it is off. */ pwm_init_state(joypad->pwm, &state); state.enabled = false; err = pwm_apply_state(joypad->pwm, &state); if (err) { dev_err(dev, "failed to apply initial PWM state: %d", err); return err; } dev_info(dev, "rumble setup success!\n"); return 0; } /*----------------------------------------------------------------------------*/ struct input_dev * joypad_input_g; void rk_send_key_f_key_up(void) { if (!joypad_input_g) return; input_report_key(joypad_input_g, BTN_MODE, 1); input_sync(joypad_input_g); } EXPORT_SYMBOL(rk_send_key_f_key_up); void rk_send_key_f_key_down(void) { if (!joypad_input_g) return; input_report_key(joypad_input_g, BTN_MODE, 0); input_sync(joypad_input_g); } EXPORT_SYMBOL(rk_send_key_f_key_down); static int joypad_input_setup(struct device *dev, struct joypad *joypad) { struct input_polled_dev *poll_dev; struct input_dev *input; int nbtn, error; u32 joypad_revision = 0; u32 joypad_product = 0; u32 boost_weak = 0; u32 boost_strong = 0; poll_dev = devm_input_allocate_polled_device(dev); if (!poll_dev) { dev_err(dev, "no memory for polled device\n"); return -ENOMEM; } poll_dev->private = joypad; poll_dev->poll = joypad_poll; poll_dev->poll_interval = joypad->poll_interval; poll_dev->open = joypad_open; poll_dev->close = joypad_close; input = poll_dev->input; joypad->input = poll_dev->input; device_property_read_string(dev, "joypad-name", &input->name); input->phys = DRV_NAME"/input0"; device_property_read_u32(dev, "joypad-revision", &joypad_revision); device_property_read_u32(dev, "joypad-product", &joypad_product); input->id.bustype = BUS_HOST; input->id.vendor = 0x484B; input->id.product = (u16)joypad_product; input->id.version = (u16)joypad_revision; /* IIO ADC key setup (0 mv ~ 1800 mv) * adc->scale */ __set_bit(EV_ABS, input->evbit); for(nbtn = 0; nbtn < joypad->amux_count; nbtn++) { struct bt_adc *adc = &joypad->adcs[nbtn]; input_set_abs_params(input, adc->report_type, adc->min, adc->max, joypad->bt_adc_fuzz, joypad->bt_adc_flat); dev_info(dev, "%s : SCALE = %d, ABS min = %d, max = %d," " fuzz = %d, flat = %d, deadzone = %d\n", __func__, adc->scale, adc->min, adc->max, joypad->bt_adc_fuzz, joypad->bt_adc_flat, joypad->bt_adc_deadzone); dev_info(dev, "%s : adc tuning_p = %d, adc_tuning_n = %d\n\n", __func__, adc->tuning_p, adc->tuning_n); } /* Rumble setip*/ device_property_read_u32(dev, "rumble-boost-weak", &boost_weak); device_property_read_u32(dev, "rumble-boost-strong", &boost_strong); joypad->boost_weak = boost_weak; joypad->boost_strong = boost_strong; dev_info(dev, "Boost = %d, %d",boost_weak, boost_strong); input_set_capability(input, EV_FF, FF_RUMBLE); error = input_ff_create_memless(input, joypad, rumble_play_effect); if (error) { dev_err(dev, "unable to register rumble, err=%d\n", error); return error; } /* GPIO key setup */ __set_bit(EV_KEY, input->evbit); for(nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { struct bt_gpio *gpio = &joypad->gpios[nbtn]; input_set_capability(input, gpio->report_type, gpio->linux_code); } if (joypad->auto_repeat) __set_bit(EV_REP, input->evbit); joypad->dev = dev; error = input_register_polled_device(poll_dev); if (error) { dev_err(dev, "unable to register polled device, err=%d\n", error); return error; } return 0; } /*----------------------------------------------------------------------------*/ static void joypad_setup_value_check(struct device *dev, struct joypad *joypad) { /* fuzz: specifies fuzz value that is used to filter noise from the event stream. */ if (g_button_adc_fuzz) joypad->bt_adc_fuzz = g_button_adc_fuzz; else device_property_read_u32(dev, "button-adc-fuzz", &joypad->bt_adc_fuzz); /* flat: values that are within this value will be discarded by joydev interface and reported as 0 instead. */ if (g_button_adc_flat) joypad->bt_adc_flat = g_button_adc_flat; else device_property_read_u32(dev, "button-adc-flat", &joypad->bt_adc_flat); /* Joystick report value control */ if (g_button_adc_scale) joypad->bt_adc_scale = g_button_adc_scale; else device_property_read_u32(dev, "button-adc-scale", &joypad->bt_adc_scale); /* Joystick deadzone value control */ if (g_button_adc_deadzone) joypad->bt_adc_deadzone = g_button_adc_deadzone; else device_property_read_u32(dev, "button-adc-deadzone", &joypad->bt_adc_deadzone); } /*----------------------------------------------------------------------------*/ static int joypad_dt_parse(struct device *dev, struct joypad *joypad) { int error = 0; /* initialize value check from boot.ini */ joypad_setup_value_check(dev, joypad); device_property_read_u32(dev, "amux-count", &joypad->amux_count); joypad->amux_dedicated = device_property_read_bool(dev, "amux-dedicated-adc"); device_property_read_u32(dev, "poll-interval", &joypad->poll_interval); joypad->auto_repeat = device_property_present(dev, "autorepeat"); /* change the report reference point? (ADC MAX - read value) */ joypad->invert_absx = device_property_present(dev, "invert-absx"); joypad->invert_absy = device_property_present(dev, "invert-absy"); joypad->invert_absrx = device_property_present(dev, "invert-absrx"); joypad->invert_absry = device_property_present(dev, "invert-absry"); dev_info(dev, "%s : invert-absx = %d, inveret-absy = %d, invert-absrx = %d, inveret-absry = %d\n", __func__, joypad->invert_absx, joypad->invert_absy, joypad->invert_absrx, joypad->invert_absry); joypad->bt_gpio_count = device_get_child_node_count(dev); if ((joypad->amux_count == 0) || (joypad->bt_gpio_count == 0)) { dev_err(dev, "adc key = %d, gpio key = %d error!", joypad->amux_count, joypad->bt_gpio_count); return -EINVAL; } error = joypad_adc_setup(dev, joypad); if (error) return error; error = joypad_amux_setup(dev, joypad); if (error) return error; error = joypad_gpio_setup(dev, joypad); if (error) return error; dev_info(dev, "%s : adc key cnt = %d, gpio key cnt = %d\n", __func__, joypad->amux_count, joypad->bt_gpio_count); return error; } static int __maybe_unused joypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); cancel_work_sync(&joypad->play_work); if (joypad->level) pwm_vibrator_stop(joypad); return 0; } static int __maybe_unused joypad_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct joypad *joypad = platform_get_drvdata(pdev); if (joypad->level) pwm_vibrator_start(joypad); return 0; } static SIMPLE_DEV_PM_OPS(joypad_pm_ops, joypad_suspend, joypad_resume); /*----------------------------------------------------------------------------*/ static int joypad_probe(struct platform_device *pdev) { struct joypad *joypad; struct device *dev = &pdev->dev; int error; joypad = devm_kzalloc(dev, sizeof(struct joypad), GFP_KERNEL); if (!joypad) { dev_err(dev, "joypad devm_kzmalloc error!"); return -ENOMEM; } /* device tree data parse */ error = joypad_dt_parse(dev, joypad); if (error) { dev_err(dev, "dt parse error!(err = %d)\n", error); return error; } mutex_init(&joypad->lock); platform_set_drvdata(pdev, joypad); error = sysfs_create_group(&pdev->dev.kobj, &joypad_attr_group); if (error) { dev_err(dev, "create sysfs group fail, error: %d\n", error); return error; } /* poll input device setup */ error = joypad_input_setup(dev, joypad); if (error) { dev_err(dev, "input setup failed!(err = %d)\n", error); return error; } /* rumble setup */ error = joypad_rumble_setup(dev, joypad); if (error) { dev_err(dev, "rumble setup failed!(err = %d)\n", error); return error; } dev_info(dev, "%s : probe success\n", __func__); return 0; } /*----------------------------------------------------------------------------*/ static const struct of_device_id joypad_of_match[] = { { .compatible = "saradc-joypad", }, {}, }; MODULE_DEVICE_TABLE(of, joypad_of_match); /*----------------------------------------------------------------------------*/ static struct platform_driver joypad_driver = { .probe = joypad_probe, .driver = { .name = DRV_NAME, .pm = &joypad_pm_ops, .of_match_table = of_match_ptr(joypad_of_match), }, }; /*----------------------------------------------------------------------------*/ static int __init joypad_init(void) { return platform_driver_register(&joypad_driver); } /*----------------------------------------------------------------------------*/ static void __exit joypad_exit(void) { platform_driver_unregister(&joypad_driver); } /*----------------------------------------------------------------------------*/ late_initcall(joypad_init); module_exit(joypad_exit);