From: James Ogletree <james.ogletree@opensource.cirrus.com> To: unlisted-recipients:; (no To-header on input) Cc: James Ogletree <james.ogletree@cirrus.com>, Dmitry Torokhov <dmitry.torokhov@gmail.com>, Rob Herring <robh+dt@kernel.org>, "Krzysztof Kozlowski" <krzysztof.kozlowski+dt@linaro.org>, Lee Jones <lee@kernel.org>, Fred Treven <fred.treven@cirrus.com>, Ben Bright <ben.bright@cirrus.com>, <linux-input@vger.kernel.org>, <devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org> Subject: [PATCH v4 4/4] Input: cs40l50 - Add support for the CS40L50 haptic driver Date: Wed, 18 Oct 2023 17:57:25 +0000 [thread overview] Message-ID: <20231018175726.3879955-5-james.ogletree@opensource.cirrus.com> (raw) In-Reply-To: <20231018175726.3879955-1-james.ogletree@opensource.cirrus.com> From: James Ogletree <james.ogletree@cirrus.com> Introduce support for Cirrus Logic Device CS40L50: a haptic driver with waveform memory, integrated DSP, and closed-loop algorithms. The input driver provides the interface for control of haptic effects through the device. Signed-off-by: James Ogletree <james.ogletree@cirrus.com> --- MAINTAINERS | 1 + drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/cs40l50-vibra.c | 353 +++++++++++++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 drivers/input/misc/cs40l50-vibra.c diff --git a/MAINTAINERS b/MAINTAINERS index 08e1e9695d49..24a00d8e5c1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4971,6 +4971,7 @@ L: patches@opensource.cirrus.com S: Supported F: Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml F: drivers/input/misc/cirrus* +F: drivers/input/misc/cs40l* F: drivers/mfd/cs40l* F: include/linux/input/cirrus* F: include/linux/mfd/cs40l* diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9f088900f863..938090648126 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -129,6 +129,16 @@ config INPUT_BMA150 To compile this driver as a module, choose M here: the module will be called bma150. +config INPUT_CS40L50_VIBRA + tristate "CS40L50 Haptic Driver support" + depends on MFD_CS40L50_CORE + help + Say Y here to enable support for Cirrus Logic's CS40L50 + haptic driver. + + To compile this driver as a module, choose M here: the + module will be called cs40l50-vibra. + config INPUT_E3X0_BUTTON tristate "NI Ettus Research USRP E3xx Button support." default n diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6abefc41037b..6b653ed2124f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o +obj-$(CONFIG_INPUT_CS40L50_VIBRA) += cs40l50-vibra.o cirrus_haptics.o obj-$(CONFIG_INPUT_DA7280_HAPTICS) += da7280.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c new file mode 100644 index 000000000000..3b3e4cb10de0 --- /dev/null +++ b/drivers/input/misc/cs40l50-vibra.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CS40L50 Advanced Haptic Driver with waveform memory, + * integrated DSP, and closed-loop algorithms + * + * Copyright 2023 Cirrus Logic, Inc. + * + */ + +#include <linux/firmware/cirrus/wmfw.h> +#include <linux/mfd/cs40l50.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +static int cs40l50_add(struct input_dev *dev, + struct ff_effect *effect, + struct ff_effect *old) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + u32 len = effect->u.periodic.custom_len; + + if (effect->type != FF_PERIODIC || effect->u.periodic.waveform != FF_CUSTOM) { + dev_err(cs40l50->dev, "Type (%#X) or waveform (%#X) unsupported\n", + effect->type, effect->u.periodic.waveform); + return -EINVAL; + } + + memcpy(&cs40l50->haptics.add_effect, effect, sizeof(struct ff_effect)); + + cs40l50->haptics.add_effect.u.periodic.custom_data = kcalloc(len, + sizeof(s16), + GFP_KERNEL); + if (!cs40l50->haptics.add_effect.u.periodic.custom_data) + return -ENOMEM; + + if (copy_from_user(cs40l50->haptics.add_effect.u.periodic.custom_data, + effect->u.periodic.custom_data, sizeof(s16) * len)) { + cs40l50->haptics.add_error = -EFAULT; + goto out_free; + } + + queue_work(cs40l50->haptics.vibe_wq, &cs40l50->haptics.add_work); + flush_work(&cs40l50->haptics.add_work); + +out_free: + kfree(cs40l50->haptics.add_effect.u.periodic.custom_data); + cs40l50->haptics.add_effect.u.periodic.custom_data = NULL; + + return cs40l50->haptics.add_error; +} + +static int cs40l50_playback(struct input_dev *dev, int effect_id, int val) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + + if (val > 0) { + cs40l50->haptics.start_effect = &dev->ff->effects[effect_id]; + queue_work(cs40l50->haptics.vibe_wq, + &cs40l50->haptics.vibe_start_work); + } else { + queue_work(cs40l50->haptics.vibe_wq, + &cs40l50->haptics.vibe_stop_work); + } + + return 0; +} + +static int cs40l50_erase(struct input_dev *dev, int effect_id) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + + cs40l50->haptics.erase_effect = &dev->ff->effects[effect_id]; + + queue_work(cs40l50->haptics.vibe_wq, &cs40l50->haptics.erase_work); + flush_work(&cs40l50->haptics.erase_work); + + return cs40l50->haptics.erase_error; +} + +static const struct reg_sequence cs40l50_int_vamp_seq[] = { + { CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER }, + { CS40L50_BLOCK_ENABLES2, CS40L50_OVERTEMP_WARN }, +}; + +static const struct reg_sequence cs40l50_irq_mask_seq[] = { + { CS40L50_IRQ1_MASK_2, CS40L50_IRQ_MASK_2_OVERRIDE }, + { CS40L50_IRQ1_MASK_20, CS40L50_IRQ_MASK_20_OVERRIDE }, +}; + +static int cs40l50_hw_init(struct cs40l50_private *cs40l50) +{ + int error; + + error = regmap_multi_reg_write(cs40l50->regmap, + cs40l50_int_vamp_seq, + ARRAY_SIZE(cs40l50_int_vamp_seq)); + if (error) + return error; + + error = cs_hap_pseq_multi_write(&cs40l50->haptics, + cs40l50_int_vamp_seq, + ARRAY_SIZE(cs40l50_int_vamp_seq), + false, PSEQ_OP_WRITE_FULL); + if (error) + return error; + + error = regmap_multi_reg_write(cs40l50->regmap, cs40l50_irq_mask_seq, + ARRAY_SIZE(cs40l50_irq_mask_seq)); + if (error) + return error; + + return cs_hap_pseq_multi_write(&cs40l50->haptics, cs40l50_irq_mask_seq, + ARRAY_SIZE(cs40l50_irq_mask_seq), false, + PSEQ_OP_WRITE_FULL); +} + +static const struct cs_dsp_client_ops cs40l50_cs_dsp_client_ops; + +static const struct cs_dsp_region cs40l50_dsp_regions[] = { + { + .type = WMFW_HALO_PM_PACKED, + .base = CS40L50_DSP1_PMEM_0 + }, + { + .type = WMFW_HALO_XM_PACKED, + .base = CS40L50_DSP1_XMEM_PACKED_0 + }, + { + .type = WMFW_HALO_YM_PACKED, + .base = CS40L50_DSP1_YMEM_PACKED_0 + }, + { + .type = WMFW_ADSP2_XM, + .base = CS40L50_DSP1_XMEM_UNPACKED24_0 + }, + { + .type = WMFW_ADSP2_YM, + .base = CS40L50_DSP1_YMEM_UNPACKED24_0 + }, +}; + +static int cs40l50_cs_dsp_init(struct cs40l50_private *cs40l50) +{ + cs40l50->dsp.num = 1; + cs40l50->dsp.type = WMFW_HALO; + cs40l50->dsp.dev = cs40l50->dev; + cs40l50->dsp.regmap = cs40l50->regmap; + cs40l50->dsp.base = CS40L50_CORE_BASE; + cs40l50->dsp.base_sysinfo = CS40L50_SYS_INFO_ID; + cs40l50->dsp.mem = cs40l50_dsp_regions; + cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions); + cs40l50->dsp.no_core_startstop = true; + cs40l50->dsp.client_ops = &cs40l50_cs_dsp_client_ops; + + return cs_dsp_halo_init(&cs40l50->dsp); +} + +static struct cs_hap_bank cs40l50_banks[] = { + { + .bank = WVFRM_BANK_RAM, + .base_index = CS40L50_RAM_BANK_INDEX_START, + .max_index = CS40L50_RAM_BANK_INDEX_START, + }, + { + .bank = WVFRM_BANK_ROM, + .base_index = CS40L50_ROM_BANK_INDEX_START, + .max_index = CS40L50_ROM_BANK_INDEX_END, + }, + { + .bank = WVFRM_BANK_OWT, + .base_index = CS40L50_RTH_INDEX_START, + .max_index = CS40L50_RTH_INDEX_END, + }, +}; + +static int cs40l50_cs_hap_init(struct cs40l50_private *cs40l50) +{ + cs40l50->haptics.regmap = cs40l50->regmap; + cs40l50->haptics.dev = cs40l50->dev; + cs40l50->haptics.banks = cs40l50_banks; + cs40l50->haptics.dsp.gpio_base_reg = CS40L50_GPIO_BASE; + cs40l50->haptics.dsp.owt_base_reg = CS40L50_OWT_BASE; + cs40l50->haptics.dsp.owt_offset_reg = CS40L50_OWT_NEXT; + cs40l50->haptics.dsp.owt_size_reg = CS40L50_OWT_SIZE; + cs40l50->haptics.dsp.mailbox_reg = CS40L50_DSP_MBOX; + cs40l50->haptics.dsp.pseq_reg = CS40L50_POWER_ON_SEQ; + cs40l50->haptics.dsp.push_owt_cmd = CS40L50_OWT_PUSH; + cs40l50->haptics.dsp.delete_owt_cmd = CS40L50_OWT_DELETE; + cs40l50->haptics.dsp.stop_cmd = CS40L50_STOP_PLAYBACK; + cs40l50->haptics.dsp.pseq_size = CS40L50_PSEQ_SIZE; + cs40l50->haptics.runtime_pm = true; + + return cs_hap_init(&cs40l50->haptics); +} + +static void cs40l50_upload_wt(const struct firmware *bin, void *context) +{ + struct cs40l50_private *cs40l50 = context; + u32 nwaves; + + mutex_lock(&cs40l50->lock); + + if (cs40l50->wmfw) { + if (regmap_write(cs40l50->regmap, CS40L50_CCM_CORE_CONTROL, + CS40L50_CLOCK_DISABLE)) + goto err_mutex; + + if (regmap_write(cs40l50->regmap, CS40L50_RAM_INIT, + CS40L50_RAM_INIT_FLAG)) + goto err_mutex; + + if (regmap_write(cs40l50->regmap, CS40L50_PWRMGT_CTL, + CS40L50_MEM_RDY_HW)) + goto err_mutex; + } + + cs_dsp_power_up(&cs40l50->dsp, cs40l50->wmfw, "cs40l50.wmfw", + bin, "cs40l50.bin", "cs40l50"); + + if (cs40l50->wmfw) { + if (regmap_write(cs40l50->regmap, CS40L50_CCM_CORE_CONTROL, + CS40L50_CLOCK_ENABLE)) + goto err_mutex; + } + + if (regmap_read(cs40l50->regmap, CS40L50_NUM_OF_WAVES, &nwaves)) + goto err_mutex; + + cs40l50->haptics.banks[WVFRM_BANK_RAM].max_index += (nwaves - 1); + +err_mutex: + mutex_unlock(&cs40l50->lock); + release_firmware(bin); + release_firmware(cs40l50->wmfw); +} + +static void cs40l50_upload_patch(const struct firmware *wmfw, void *context) +{ + struct cs40l50_private *cs40l50 = context; + + cs40l50->wmfw = wmfw; + + if (request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_WT, + cs40l50->dev, GFP_KERNEL, + cs40l50, cs40l50_upload_wt)) + release_firmware(cs40l50->wmfw); +} + +static int cs40l50_input_init(struct cs40l50_private *cs40l50) +{ + int error; + + cs40l50->input = devm_input_allocate_device(cs40l50->dev); + if (!cs40l50->input) + return -ENOMEM; + + cs40l50->input->id.product = cs40l50->devid & 0xFFFF; + cs40l50->input->id.version = cs40l50->revid; + cs40l50->input->name = "cs40l50_vibra"; + + input_set_drvdata(cs40l50->input, cs40l50); + input_set_capability(cs40l50->input, EV_FF, FF_PERIODIC); + input_set_capability(cs40l50->input, EV_FF, FF_CUSTOM); + + error = input_ff_create(cs40l50->input, FF_MAX_EFFECTS); + if (error) + return error; + + cs40l50->input->ff->upload = cs40l50_add; + cs40l50->input->ff->playback = cs40l50_playback; + cs40l50->input->ff->erase = cs40l50_erase; + + INIT_LIST_HEAD(&cs40l50->haptics.effect_head); + + error = input_register_device(cs40l50->input); + if (error) + goto err_free_dev; + + return cs40l50_cs_hap_init(cs40l50); + +err_free_dev: + input_free_device(cs40l50->input); + return error; +} +static int cs40l50_vibra_probe(struct platform_device *pdev) +{ + struct cs40l50_private *cs40l50 = dev_get_drvdata(pdev->dev.parent); + int error; + + error = cs40l50_input_init(cs40l50); + if (error) + return error; + + error = cs40l50_hw_init(cs40l50); + if (error) + goto err_input; + + error = cs40l50_cs_dsp_init(cs40l50); + if (error) + goto err_input; + + error = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + CS40L50_FW, cs40l50->dev, + GFP_KERNEL, cs40l50, + cs40l50_upload_patch); + if (error) + goto err_dsp; + + return 0; + +err_dsp: + cs_dsp_remove(&cs40l50->dsp); +err_input: + input_unregister_device(cs40l50->input); + cs_hap_remove(&cs40l50->haptics); + + return error; +} + +static int cs40l50_vibra_remove(struct platform_device *pdev) +{ + struct cs40l50_private *cs40l50 = dev_get_drvdata(pdev->dev.parent); + + input_unregister_device(cs40l50->input); + cs_hap_remove(&cs40l50->haptics); + + if (cs40l50->dsp.booted) + cs_dsp_power_down(&cs40l50->dsp); + if (&cs40l50->dsp) + cs_dsp_remove(&cs40l50->dsp); + + return 0; +} + +static const struct platform_device_id cs40l50_id_vibra[] = { + {"cs40l50-vibra", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs40l50_id_vibra); + +static struct platform_driver cs40l50_vibra_driver = { + .probe = cs40l50_vibra_probe, + .remove = cs40l50_vibra_remove, + .id_table = cs40l50_id_vibra, + .driver = { + .name = "cs40l50-vibra", + }, +}; +module_platform_driver(cs40l50_vibra_driver); + +MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver"); +MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>"); +MODULE_LICENSE("GPL"); -- 2.25.1
WARNING: multiple messages have this Message-ID (diff)
From: James Ogletree <james.ogletree@opensource.cirrus.com> Cc: James Ogletree <james.ogletree@cirrus.com>, Dmitry Torokhov <dmitry.torokhov@gmail.com>, Rob Herring <robh+dt@kernel.org>, "Krzysztof Kozlowski" <krzysztof.kozlowski+dt@linaro.org>, Lee Jones <lee@kernel.org>, Fred Treven <fred.treven@cirrus.com>, Ben Bright <ben.bright@cirrus.com>, <linux-input@vger.kernel.org>, <devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org> Subject: [PATCH v4 4/4] Input: cs40l50 - Add support for the CS40L50 haptic driver Date: Wed, 18 Oct 2023 17:57:25 +0000 [thread overview] Message-ID: <20231018175726.3879955-5-james.ogletree@opensource.cirrus.com> (raw) In-Reply-To: <20231018175726.3879955-1-james.ogletree@opensource.cirrus.com> From: James Ogletree <james.ogletree@cirrus.com> Introduce support for Cirrus Logic Device CS40L50: a haptic driver with waveform memory, integrated DSP, and closed-loop algorithms. The input driver provides the interface for control of haptic effects through the device. Signed-off-by: James Ogletree <james.ogletree@cirrus.com> --- MAINTAINERS | 1 + drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/cs40l50-vibra.c | 353 +++++++++++++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 drivers/input/misc/cs40l50-vibra.c diff --git a/MAINTAINERS b/MAINTAINERS index 08e1e9695d49..24a00d8e5c1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4971,6 +4971,7 @@ L: patches@opensource.cirrus.com S: Supported F: Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml F: drivers/input/misc/cirrus* +F: drivers/input/misc/cs40l* F: drivers/mfd/cs40l* F: include/linux/input/cirrus* F: include/linux/mfd/cs40l* diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9f088900f863..938090648126 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -129,6 +129,16 @@ config INPUT_BMA150 To compile this driver as a module, choose M here: the module will be called bma150. +config INPUT_CS40L50_VIBRA + tristate "CS40L50 Haptic Driver support" + depends on MFD_CS40L50_CORE + help + Say Y here to enable support for Cirrus Logic's CS40L50 + haptic driver. + + To compile this driver as a module, choose M here: the + module will be called cs40l50-vibra. + config INPUT_E3X0_BUTTON tristate "NI Ettus Research USRP E3xx Button support." default n diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6abefc41037b..6b653ed2124f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o +obj-$(CONFIG_INPUT_CS40L50_VIBRA) += cs40l50-vibra.o cirrus_haptics.o obj-$(CONFIG_INPUT_DA7280_HAPTICS) += da7280.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c new file mode 100644 index 000000000000..3b3e4cb10de0 --- /dev/null +++ b/drivers/input/misc/cs40l50-vibra.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CS40L50 Advanced Haptic Driver with waveform memory, + * integrated DSP, and closed-loop algorithms + * + * Copyright 2023 Cirrus Logic, Inc. + * + */ + +#include <linux/firmware/cirrus/wmfw.h> +#include <linux/mfd/cs40l50.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +static int cs40l50_add(struct input_dev *dev, + struct ff_effect *effect, + struct ff_effect *old) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + u32 len = effect->u.periodic.custom_len; + + if (effect->type != FF_PERIODIC || effect->u.periodic.waveform != FF_CUSTOM) { + dev_err(cs40l50->dev, "Type (%#X) or waveform (%#X) unsupported\n", + effect->type, effect->u.periodic.waveform); + return -EINVAL; + } + + memcpy(&cs40l50->haptics.add_effect, effect, sizeof(struct ff_effect)); + + cs40l50->haptics.add_effect.u.periodic.custom_data = kcalloc(len, + sizeof(s16), + GFP_KERNEL); + if (!cs40l50->haptics.add_effect.u.periodic.custom_data) + return -ENOMEM; + + if (copy_from_user(cs40l50->haptics.add_effect.u.periodic.custom_data, + effect->u.periodic.custom_data, sizeof(s16) * len)) { + cs40l50->haptics.add_error = -EFAULT; + goto out_free; + } + + queue_work(cs40l50->haptics.vibe_wq, &cs40l50->haptics.add_work); + flush_work(&cs40l50->haptics.add_work); + +out_free: + kfree(cs40l50->haptics.add_effect.u.periodic.custom_data); + cs40l50->haptics.add_effect.u.periodic.custom_data = NULL; + + return cs40l50->haptics.add_error; +} + +static int cs40l50_playback(struct input_dev *dev, int effect_id, int val) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + + if (val > 0) { + cs40l50->haptics.start_effect = &dev->ff->effects[effect_id]; + queue_work(cs40l50->haptics.vibe_wq, + &cs40l50->haptics.vibe_start_work); + } else { + queue_work(cs40l50->haptics.vibe_wq, + &cs40l50->haptics.vibe_stop_work); + } + + return 0; +} + +static int cs40l50_erase(struct input_dev *dev, int effect_id) +{ + struct cs40l50_private *cs40l50 = input_get_drvdata(dev); + + cs40l50->haptics.erase_effect = &dev->ff->effects[effect_id]; + + queue_work(cs40l50->haptics.vibe_wq, &cs40l50->haptics.erase_work); + flush_work(&cs40l50->haptics.erase_work); + + return cs40l50->haptics.erase_error; +} + +static const struct reg_sequence cs40l50_int_vamp_seq[] = { + { CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER }, + { CS40L50_BLOCK_ENABLES2, CS40L50_OVERTEMP_WARN }, +}; + +static const struct reg_sequence cs40l50_irq_mask_seq[] = { + { CS40L50_IRQ1_MASK_2, CS40L50_IRQ_MASK_2_OVERRIDE }, + { CS40L50_IRQ1_MASK_20, CS40L50_IRQ_MASK_20_OVERRIDE }, +}; + +static int cs40l50_hw_init(struct cs40l50_private *cs40l50) +{ + int error; + + error = regmap_multi_reg_write(cs40l50->regmap, + cs40l50_int_vamp_seq, + ARRAY_SIZE(cs40l50_int_vamp_seq)); + if (error) + return error; + + error = cs_hap_pseq_multi_write(&cs40l50->haptics, + cs40l50_int_vamp_seq, + ARRAY_SIZE(cs40l50_int_vamp_seq), + false, PSEQ_OP_WRITE_FULL); + if (error) + return error; + + error = regmap_multi_reg_write(cs40l50->regmap, cs40l50_irq_mask_seq, + ARRAY_SIZE(cs40l50_irq_mask_seq)); + if (error) + return error; + + return cs_hap_pseq_multi_write(&cs40l50->haptics, cs40l50_irq_mask_seq, + ARRAY_SIZE(cs40l50_irq_mask_seq), false, + PSEQ_OP_WRITE_FULL); +} + +static const struct cs_dsp_client_ops cs40l50_cs_dsp_client_ops; + +static const struct cs_dsp_region cs40l50_dsp_regions[] = { + { + .type = WMFW_HALO_PM_PACKED, + .base = CS40L50_DSP1_PMEM_0 + }, + { + .type = WMFW_HALO_XM_PACKED, + .base = CS40L50_DSP1_XMEM_PACKED_0 + }, + { + .type = WMFW_HALO_YM_PACKED, + .base = CS40L50_DSP1_YMEM_PACKED_0 + }, + { + .type = WMFW_ADSP2_XM, + .base = CS40L50_DSP1_XMEM_UNPACKED24_0 + }, + { + .type = WMFW_ADSP2_YM, + .base = CS40L50_DSP1_YMEM_UNPACKED24_0 + }, +}; + +static int cs40l50_cs_dsp_init(struct cs40l50_private *cs40l50) +{ + cs40l50->dsp.num = 1; + cs40l50->dsp.type = WMFW_HALO; + cs40l50->dsp.dev = cs40l50->dev; + cs40l50->dsp.regmap = cs40l50->regmap; + cs40l50->dsp.base = CS40L50_CORE_BASE; + cs40l50->dsp.base_sysinfo = CS40L50_SYS_INFO_ID; + cs40l50->dsp.mem = cs40l50_dsp_regions; + cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions); + cs40l50->dsp.no_core_startstop = true; + cs40l50->dsp.client_ops = &cs40l50_cs_dsp_client_ops; + + return cs_dsp_halo_init(&cs40l50->dsp); +} + +static struct cs_hap_bank cs40l50_banks[] = { + { + .bank = WVFRM_BANK_RAM, + .base_index = CS40L50_RAM_BANK_INDEX_START, + .max_index = CS40L50_RAM_BANK_INDEX_START, + }, + { + .bank = WVFRM_BANK_ROM, + .base_index = CS40L50_ROM_BANK_INDEX_START, + .max_index = CS40L50_ROM_BANK_INDEX_END, + }, + { + .bank = WVFRM_BANK_OWT, + .base_index = CS40L50_RTH_INDEX_START, + .max_index = CS40L50_RTH_INDEX_END, + }, +}; + +static int cs40l50_cs_hap_init(struct cs40l50_private *cs40l50) +{ + cs40l50->haptics.regmap = cs40l50->regmap; + cs40l50->haptics.dev = cs40l50->dev; + cs40l50->haptics.banks = cs40l50_banks; + cs40l50->haptics.dsp.gpio_base_reg = CS40L50_GPIO_BASE; + cs40l50->haptics.dsp.owt_base_reg = CS40L50_OWT_BASE; + cs40l50->haptics.dsp.owt_offset_reg = CS40L50_OWT_NEXT; + cs40l50->haptics.dsp.owt_size_reg = CS40L50_OWT_SIZE; + cs40l50->haptics.dsp.mailbox_reg = CS40L50_DSP_MBOX; + cs40l50->haptics.dsp.pseq_reg = CS40L50_POWER_ON_SEQ; + cs40l50->haptics.dsp.push_owt_cmd = CS40L50_OWT_PUSH; + cs40l50->haptics.dsp.delete_owt_cmd = CS40L50_OWT_DELETE; + cs40l50->haptics.dsp.stop_cmd = CS40L50_STOP_PLAYBACK; + cs40l50->haptics.dsp.pseq_size = CS40L50_PSEQ_SIZE; + cs40l50->haptics.runtime_pm = true; + + return cs_hap_init(&cs40l50->haptics); +} + +static void cs40l50_upload_wt(const struct firmware *bin, void *context) +{ + struct cs40l50_private *cs40l50 = context; + u32 nwaves; + + mutex_lock(&cs40l50->lock); + + if (cs40l50->wmfw) { + if (regmap_write(cs40l50->regmap, CS40L50_CCM_CORE_CONTROL, + CS40L50_CLOCK_DISABLE)) + goto err_mutex; + + if (regmap_write(cs40l50->regmap, CS40L50_RAM_INIT, + CS40L50_RAM_INIT_FLAG)) + goto err_mutex; + + if (regmap_write(cs40l50->regmap, CS40L50_PWRMGT_CTL, + CS40L50_MEM_RDY_HW)) + goto err_mutex; + } + + cs_dsp_power_up(&cs40l50->dsp, cs40l50->wmfw, "cs40l50.wmfw", + bin, "cs40l50.bin", "cs40l50"); + + if (cs40l50->wmfw) { + if (regmap_write(cs40l50->regmap, CS40L50_CCM_CORE_CONTROL, + CS40L50_CLOCK_ENABLE)) + goto err_mutex; + } + + if (regmap_read(cs40l50->regmap, CS40L50_NUM_OF_WAVES, &nwaves)) + goto err_mutex; + + cs40l50->haptics.banks[WVFRM_BANK_RAM].max_index += (nwaves - 1); + +err_mutex: + mutex_unlock(&cs40l50->lock); + release_firmware(bin); + release_firmware(cs40l50->wmfw); +} + +static void cs40l50_upload_patch(const struct firmware *wmfw, void *context) +{ + struct cs40l50_private *cs40l50 = context; + + cs40l50->wmfw = wmfw; + + if (request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_WT, + cs40l50->dev, GFP_KERNEL, + cs40l50, cs40l50_upload_wt)) + release_firmware(cs40l50->wmfw); +} + +static int cs40l50_input_init(struct cs40l50_private *cs40l50) +{ + int error; + + cs40l50->input = devm_input_allocate_device(cs40l50->dev); + if (!cs40l50->input) + return -ENOMEM; + + cs40l50->input->id.product = cs40l50->devid & 0xFFFF; + cs40l50->input->id.version = cs40l50->revid; + cs40l50->input->name = "cs40l50_vibra"; + + input_set_drvdata(cs40l50->input, cs40l50); + input_set_capability(cs40l50->input, EV_FF, FF_PERIODIC); + input_set_capability(cs40l50->input, EV_FF, FF_CUSTOM); + + error = input_ff_create(cs40l50->input, FF_MAX_EFFECTS); + if (error) + return error; + + cs40l50->input->ff->upload = cs40l50_add; + cs40l50->input->ff->playback = cs40l50_playback; + cs40l50->input->ff->erase = cs40l50_erase; + + INIT_LIST_HEAD(&cs40l50->haptics.effect_head); + + error = input_register_device(cs40l50->input); + if (error) + goto err_free_dev; + + return cs40l50_cs_hap_init(cs40l50); + +err_free_dev: + input_free_device(cs40l50->input); + return error; +} +static int cs40l50_vibra_probe(struct platform_device *pdev) +{ + struct cs40l50_private *cs40l50 = dev_get_drvdata(pdev->dev.parent); + int error; + + error = cs40l50_input_init(cs40l50); + if (error) + return error; + + error = cs40l50_hw_init(cs40l50); + if (error) + goto err_input; + + error = cs40l50_cs_dsp_init(cs40l50); + if (error) + goto err_input; + + error = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + CS40L50_FW, cs40l50->dev, + GFP_KERNEL, cs40l50, + cs40l50_upload_patch); + if (error) + goto err_dsp; + + return 0; + +err_dsp: + cs_dsp_remove(&cs40l50->dsp); +err_input: + input_unregister_device(cs40l50->input); + cs_hap_remove(&cs40l50->haptics); + + return error; +} + +static int cs40l50_vibra_remove(struct platform_device *pdev) +{ + struct cs40l50_private *cs40l50 = dev_get_drvdata(pdev->dev.parent); + + input_unregister_device(cs40l50->input); + cs_hap_remove(&cs40l50->haptics); + + if (cs40l50->dsp.booted) + cs_dsp_power_down(&cs40l50->dsp); + if (&cs40l50->dsp) + cs_dsp_remove(&cs40l50->dsp); + + return 0; +} + +static const struct platform_device_id cs40l50_id_vibra[] = { + {"cs40l50-vibra", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs40l50_id_vibra); + +static struct platform_driver cs40l50_vibra_driver = { + .probe = cs40l50_vibra_probe, + .remove = cs40l50_vibra_remove, + .id_table = cs40l50_id_vibra, + .driver = { + .name = "cs40l50-vibra", + }, +}; +module_platform_driver(cs40l50_vibra_driver); + +MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver"); +MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>"); +MODULE_LICENSE("GPL"); -- 2.25.1
next prev parent reply other threads:[~2023-10-18 17:58 UTC|newest] Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top 2023-10-18 17:57 [PATCH v4 0/4] Add support for CS40L50 James Ogletree 2023-10-18 17:57 ` James Ogletree 2023-10-18 17:57 ` [PATCH v4 1/4] dt-bindings: input: cirrus,cs40l50: Add initial DT binding James Ogletree 2023-10-18 17:57 ` James Ogletree 2023-10-18 19:21 ` Krzysztof Kozlowski 2023-10-18 21:44 ` James Ogletree 2023-10-18 17:57 ` [PATCH v4 2/4] Input: cs40l50 - Add cirrus haptics base support James Ogletree 2023-10-18 17:57 ` James Ogletree 2023-10-25 2:04 ` Jeff LaBundy 2023-11-01 20:46 ` James Ogletree 2023-11-26 0:52 ` Jeff LaBundy 2023-11-29 22:22 ` James Ogletree 2023-12-14 2:11 ` Jeff LaBundy 2023-10-18 17:57 ` [PATCH v4 3/4] mfd: cs40l50: Add support for CS40L50 core driver James Ogletree 2023-10-18 17:57 ` James Ogletree 2023-10-19 16:23 ` Lee Jones 2023-10-20 15:39 ` James Ogletree 2023-10-23 9:20 ` Lee Jones 2023-10-24 1:08 ` Jeff LaBundy 2023-10-24 1:30 ` James Ogletree 2023-10-24 15:47 ` Lee Jones 2023-10-24 1:14 ` James Ogletree 2023-10-21 14:56 ` kernel test robot 2023-10-25 2:56 ` Jeff LaBundy 2023-11-01 20:47 ` James Ogletree 2023-11-26 1:03 ` Jeff LaBundy 2023-10-25 3:20 ` Jeff LaBundy 2023-10-25 9:26 ` Lee Jones 2023-10-18 17:57 ` James Ogletree [this message] 2023-10-18 17:57 ` [PATCH v4 4/4] Input: cs40l50 - Add support for the CS40L50 haptic driver James Ogletree 2023-10-20 15:30 ` kernel test robot 2023-10-25 3:03 ` Jeff LaBundy 2023-11-01 20:47 ` James Ogletree 2023-11-26 1:11 ` Jeff LaBundy
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20231018175726.3879955-5-james.ogletree@opensource.cirrus.com \ --to=james.ogletree@opensource.cirrus.com \ --cc=ben.bright@cirrus.com \ --cc=devicetree@vger.kernel.org \ --cc=dmitry.torokhov@gmail.com \ --cc=fred.treven@cirrus.com \ --cc=james.ogletree@cirrus.com \ --cc=krzysztof.kozlowski+dt@linaro.org \ --cc=lee@kernel.org \ --cc=linux-input@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=robh+dt@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.