vibrator: cs40l25: add pwle_ramp_down feature

Add the pwle_ramp_down feature and set the ramp down time to be 50 ms to
match with frameworks

Bug: 200713702
Test: verified that pwle_ramp_down node works as expected, there is no
click effect when a waveform is cancelled and regression testing passed

Change-Id: Id75cf8c4456b22887135bd7bf206516f26415157
Signed-off-by: Vince Leung <leungv@google.com>
diff --git a/vibrator/cs40l25/Hardware.h b/vibrator/cs40l25/Hardware.h
index d1883e5..f74fe93 100644
--- a/vibrator/cs40l25/Hardware.h
+++ b/vibrator/cs40l25/Hardware.h
@@ -48,6 +48,7 @@
         open("device/clab_enable", &mClabEnable);
         open("device/available_pwle_segments", &mAvailablePwleSegments);
         open("device/pwle", &mPwle);
+        open("device/pwle_ramp_down", &mPwleRampDown);
     }
 
     bool setF0(uint32_t value) override { return set(value, &mF0); }
@@ -76,6 +77,7 @@
     bool getAvailablePwleSegments(uint32_t *value) override { return get(value, &mAvailablePwleSegments); }
     bool hasPwle() override { return has(mPwle); }
     bool setPwle(std::string value) override { return set(value, &mPwle); }
+    bool setPwleRampDown(uint32_t value) override { return set(value, &mPwleRampDown); }
     void debug(int fd) override { HwApiBase::debug(fd); }
 
   private:
@@ -101,6 +103,7 @@
     std::ofstream mClabEnable;
     std::ifstream mAvailablePwleSegments;
     std::ofstream mPwle;
+    std::ofstream mPwleRampDown;
 };
 
 class HwCal : public Vibrator::HwCal, private HwCalBase {
diff --git a/vibrator/cs40l25/Vibrator.cpp b/vibrator/cs40l25/Vibrator.cpp
index 350c999..8110cb3 100644
--- a/vibrator/cs40l25/Vibrator.cpp
+++ b/vibrator/cs40l25/Vibrator.cpp
@@ -101,6 +101,8 @@
 static constexpr float PWLE_FREQUENCY_MAX_HZ = 1023.75;
 static constexpr float PWLE_BW_MAP_SIZE =
     1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
+static constexpr float RAMP_DOWN_CONSTANT = 1048.576;
+static constexpr float RAMP_DOWN_TIME_MS = 50.0;
 
 static struct pcm_config haptic_nohost_config = {
     .channels = 1,
@@ -233,6 +235,7 @@
 
     createPwleMaxLevelLimitMap();
     mIsUnderExternalControl = false;
+    setPwleRampDown();
 }
 
 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@@ -1147,6 +1150,22 @@
     return false;
 }
 
+void Vibrator::setPwleRampDown() {
+    // The formula for calculating the ramp down coefficient to be written into
+    // pwle_ramp_down is as follows:
+    //    Crd = 1048.576 / Trd
+    // where Trd is the desired ramp down time in seconds
+    // pwle_ramp_down accepts only 24 bit integers values
+
+    const float seconds = RAMP_DOWN_TIME_MS / 1000;
+    const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds);
+
+    if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) {
+        ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno,
+              strerror(errno));
+    }
+}
+
 }  // namespace vibrator
 }  // namespace hardware
 }  // namespace android
diff --git a/vibrator/cs40l25/Vibrator.h b/vibrator/cs40l25/Vibrator.h
index 08a115e..88f7abd 100644
--- a/vibrator/cs40l25/Vibrator.h
+++ b/vibrator/cs40l25/Vibrator.h
@@ -101,6 +101,9 @@
         // Specifies piecewise-linear specifications to generate complex
         // waveforms.
         virtual bool setPwle(std::string value) = 0;
+        // Specifies the coefficient required for a ramp down when a waveform
+        // ends
+        virtual bool setPwleRampDown(uint32_t value) = 0;
         // Emit diagnostic information to the given file.
         virtual void debug(int fd) = 0;
     };
@@ -200,6 +203,7 @@
     bool hasHapticAlsaDevice();
     bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
     void createPwleMaxLevelLimitMap();
+    void setPwleRampDown();
 
     std::unique_ptr<HwApi> mHwApi;
     std::unique_ptr<HwCal> mHwCal;
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
index 3f28d81..f373d31 100644
--- a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
@@ -34,6 +34,7 @@
     chown system system /sys/class/leds/vibrator/device/hw_reset
     chown system system /sys/class/leds/vibrator/device/num_waves
     chown system system /sys/class/leds/vibrator/device/pwle
+    chown system system /sys/class/leds/vibrator/device/pwle_ramp_down
     chown system system /sys/class/leds/vibrator/device/q_stored
     chown system system /sys/class/leds/vibrator/device/redc_comp_enable
     chown system system /sys/class/leds/vibrator/device/redc_stored
@@ -67,6 +68,7 @@
         device/heartbeat
         device/num_waves
         device/pwle
+        device/pwle_ramp_down
         device/q_stored
         device/redc_stored
         state
diff --git a/vibrator/cs40l25/tests/mocks.h b/vibrator/cs40l25/tests/mocks.h
index 2c69e27..8f2c672 100644
--- a/vibrator/cs40l25/tests/mocks.h
+++ b/vibrator/cs40l25/tests/mocks.h
@@ -49,6 +49,7 @@
     MOCK_METHOD1(getAvailablePwleSegments, bool(uint32_t *value));
     MOCK_METHOD0(hasPwle, bool());
     MOCK_METHOD1(setPwle, bool(std::string value));
+    MOCK_METHOD1(setPwleRampDown, bool(uint32_t value));
     MOCK_METHOD1(debug, void(int fd));
 
     ~MockApi() override { destructor(); };
diff --git a/vibrator/cs40l25/tests/test-hwapi.cpp b/vibrator/cs40l25/tests/test-hwapi.cpp
index 7f2ae01..a339207 100644
--- a/vibrator/cs40l25/tests/test-hwapi.cpp
+++ b/vibrator/cs40l25/tests/test-hwapi.cpp
@@ -55,6 +55,7 @@
             "device/num_waves",
             "device/available_pwle_segments",
             "device/pwle",
+            "device/pwle_ramp_down",
     };
 
   public:
@@ -319,6 +320,8 @@
                                          &Vibrator::HwApi::setGpioRiseIndex),
                 SetUint32Test::MakeParam("device/gpio1_rise_dig_scale",
                                          &Vibrator::HwApi::setGpioRiseScale),
+                SetUint32Test::MakeParam("device/pwle_ramp_down",
+                                         &Vibrator::HwApi::setPwleRampDown),
         }),
         SetUint32Test::PrintParam);