Message ID | 20231130013615.14287-5-quic_amelende@quicinc.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add support for LUT PPG | expand |
Hi Anjelique, kernel test robot noticed the following build warnings: https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Anjelique-Melendez/dt-bindings-soc-qcom-Add-qcom-pbs-bindings/20231130-094701 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next patch link: https://lore.kernel.org/r/20231130013615.14287-5-quic_amelende%40quicinc.com patch subject: [PATCH v7 4/7] leds: rgb: leds-qcom-lpg: Add support for PPG through single SDAM config: csky-randconfig-r071-20231130 (https://download.01.org/0day-ci/archive/20231130/202311302200.RcTP9m0Y-lkp@intel.com/config) compiler: csky-linux-gcc (GCC) 13.2.0 reproduce: (https://download.01.org/0day-ci/archive/20231130/202311302200.RcTP9m0Y-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <error27@gmail.com> | Closes: https://lore.kernel.org/r/202311302200.RcTP9m0Y-lkp@intel.com/ smatch warnings: drivers/leds/rgb/leds-qcom-lpg.c:1055 lpg_pattern_set() error: uninitialized symbol 'lo_pause'. drivers/leds/rgb/leds-qcom-lpg.c:1056 lpg_pattern_set() error: uninitialized symbol 'hi_pause'. vim +/lo_pause +1055 drivers/leds/rgb/leds-qcom-lpg.c e98a860f65428a Bjorn Andersson 2022-05-23 904 static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, 24e2d05d1b6898 Bjorn Andersson 2022-03-03 905 u32 len, int repeat) 24e2d05d1b6898 Bjorn Andersson 2022-03-03 906 { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 907 struct lpg_channel *chan; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 908 struct lpg *lpg = led->lpg; e98a860f65428a Bjorn Andersson 2022-05-23 909 struct led_pattern *pattern; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 910 unsigned int brightness_a; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 911 unsigned int brightness_b; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 912 unsigned int actual_len; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 913 unsigned int hi_pause; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 914 unsigned int lo_pause; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 915 unsigned int delta_t; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 916 unsigned int lo_idx; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 917 unsigned int hi_idx; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 918 unsigned int i; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 919 bool ping_pong = true; e98a860f65428a Bjorn Andersson 2022-05-23 920 int ret = -EINVAL; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 921 24e2d05d1b6898 Bjorn Andersson 2022-03-03 922 /* Hardware only support oneshot or indefinite loops */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 923 if (repeat != -1 && repeat != 1) 24e2d05d1b6898 Bjorn Andersson 2022-03-03 924 return -EINVAL; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 925 e98a860f65428a Bjorn Andersson 2022-05-23 926 /* e98a860f65428a Bjorn Andersson 2022-05-23 927 * The standardized leds-trigger-pattern format defines that the e98a860f65428a Bjorn Andersson 2022-05-23 928 * brightness of the LED follows a linear transition from one entry e98a860f65428a Bjorn Andersson 2022-05-23 929 * in the pattern to the next, over the given delta_t time. It e98a860f65428a Bjorn Andersson 2022-05-23 930 * describes that the way to perform instant transitions a zero-length e98a860f65428a Bjorn Andersson 2022-05-23 931 * entry should be added following a pattern entry. e98a860f65428a Bjorn Andersson 2022-05-23 932 * e98a860f65428a Bjorn Andersson 2022-05-23 933 * The LPG hardware is only able to perform the latter (no linear e98a860f65428a Bjorn Andersson 2022-05-23 934 * transitions), so require each entry in the pattern to be followed by e98a860f65428a Bjorn Andersson 2022-05-23 935 * a zero-length transition. e98a860f65428a Bjorn Andersson 2022-05-23 936 */ e98a860f65428a Bjorn Andersson 2022-05-23 937 if (len % 2) e98a860f65428a Bjorn Andersson 2022-05-23 938 return -EINVAL; e98a860f65428a Bjorn Andersson 2022-05-23 939 e98a860f65428a Bjorn Andersson 2022-05-23 940 pattern = kcalloc(len / 2, sizeof(*pattern), GFP_KERNEL); e98a860f65428a Bjorn Andersson 2022-05-23 941 if (!pattern) e98a860f65428a Bjorn Andersson 2022-05-23 942 return -ENOMEM; e98a860f65428a Bjorn Andersson 2022-05-23 943 e98a860f65428a Bjorn Andersson 2022-05-23 944 for (i = 0; i < len; i += 2) { e98a860f65428a Bjorn Andersson 2022-05-23 945 if (led_pattern[i].brightness != led_pattern[i + 1].brightness) e98a860f65428a Bjorn Andersson 2022-05-23 946 goto out_free_pattern; e98a860f65428a Bjorn Andersson 2022-05-23 947 if (led_pattern[i + 1].delta_t != 0) e98a860f65428a Bjorn Andersson 2022-05-23 948 goto out_free_pattern; e98a860f65428a Bjorn Andersson 2022-05-23 949 e98a860f65428a Bjorn Andersson 2022-05-23 950 pattern[i / 2].brightness = led_pattern[i].brightness; e98a860f65428a Bjorn Andersson 2022-05-23 951 pattern[i / 2].delta_t = led_pattern[i].delta_t; e98a860f65428a Bjorn Andersson 2022-05-23 952 } e98a860f65428a Bjorn Andersson 2022-05-23 953 e98a860f65428a Bjorn Andersson 2022-05-23 954 len /= 2; e98a860f65428a Bjorn Andersson 2022-05-23 955 24e2d05d1b6898 Bjorn Andersson 2022-03-03 956 /* 24e2d05d1b6898 Bjorn Andersson 2022-03-03 957 * Specifying a pattern of length 1 causes the hardware to iterate 24e2d05d1b6898 Bjorn Andersson 2022-03-03 958 * through the entire LUT, so prohibit this. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 959 */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 960 if (len < 2) e98a860f65428a Bjorn Andersson 2022-05-23 961 goto out_free_pattern; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 962 24e2d05d1b6898 Bjorn Andersson 2022-03-03 963 /* 24e2d05d1b6898 Bjorn Andersson 2022-03-03 964 * The LPG plays patterns with at a fixed pace, a "low pause" can be 24e2d05d1b6898 Bjorn Andersson 2022-03-03 965 * used to stretch the first delay of the pattern and a "high pause" 24e2d05d1b6898 Bjorn Andersson 2022-03-03 966 * the last one. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 967 * 24e2d05d1b6898 Bjorn Andersson 2022-03-03 968 * In order to save space the pattern can be played in "ping pong" 24e2d05d1b6898 Bjorn Andersson 2022-03-03 969 * mode, in which the pattern is first played forward, then "high 24e2d05d1b6898 Bjorn Andersson 2022-03-03 970 * pause" is applied, then the pattern is played backwards and finally 24e2d05d1b6898 Bjorn Andersson 2022-03-03 971 * the "low pause" is applied. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 972 * 24e2d05d1b6898 Bjorn Andersson 2022-03-03 973 * The middle elements of the pattern are used to determine delta_t and 24e2d05d1b6898 Bjorn Andersson 2022-03-03 974 * the "low pause" and "high pause" multipliers are derrived from this. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 975 * 24e2d05d1b6898 Bjorn Andersson 2022-03-03 976 * The first element in the pattern is used to determine "low pause". 24e2d05d1b6898 Bjorn Andersson 2022-03-03 977 * 24e2d05d1b6898 Bjorn Andersson 2022-03-03 978 * If the specified pattern is a palindrome the ping pong mode is 24e2d05d1b6898 Bjorn Andersson 2022-03-03 979 * enabled. In this scenario the delta_t of the middle entry (i.e. the 24e2d05d1b6898 Bjorn Andersson 2022-03-03 980 * last in the programmed pattern) determines the "high pause". 05c1d2a258599b Anjelique Melendez 2023-11-29 981 * 05c1d2a258599b Anjelique Melendez 2023-11-29 982 * SDAM-based devices do not support "ping-pong", "low pause" or "high pause" 24e2d05d1b6898 Bjorn Andersson 2022-03-03 983 */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 984 24e2d05d1b6898 Bjorn Andersson 2022-03-03 985 /* Detect palindromes and use "ping pong" to reduce LUT usage */ 05c1d2a258599b Anjelique Melendez 2023-11-29 986 if (lpg->lut_base) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 987 for (i = 0; i < len / 2; i++) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 988 brightness_a = pattern[i].brightness; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 989 brightness_b = pattern[len - i - 1].brightness; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 990 24e2d05d1b6898 Bjorn Andersson 2022-03-03 991 if (brightness_a != brightness_b) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 992 ping_pong = false; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 993 break; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 994 } 24e2d05d1b6898 Bjorn Andersson 2022-03-03 995 } 05c1d2a258599b Anjelique Melendez 2023-11-29 996 } else 05c1d2a258599b Anjelique Melendez 2023-11-29 997 ping_pong = false; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 998 24e2d05d1b6898 Bjorn Andersson 2022-03-03 999 /* The pattern length to be written to the LUT */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1000 if (ping_pong) 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1001 actual_len = (len + 1) / 2; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1002 else 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1003 actual_len = len; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1004 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1005 /* 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1006 * Validate that all delta_t in the pattern are the same, with the 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1007 * exception of the middle element in case of ping_pong. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1008 */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1009 delta_t = pattern[1].delta_t; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1010 for (i = 2; i < len; i++) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1011 if (pattern[i].delta_t != delta_t) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1012 /* 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1013 * Allow last entry in the full or shortened pattern to 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1014 * specify hi pause. Reject other variations. 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1015 */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1016 if (i != actual_len - 1) e98a860f65428a Bjorn Andersson 2022-05-23 1017 goto out_free_pattern; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1018 } 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1019 } 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1020 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1021 /* LPG_RAMP_DURATION_REG is a 9bit */ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1022 if (delta_t >= BIT(9)) e98a860f65428a Bjorn Andersson 2022-05-23 1023 goto out_free_pattern; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1024 05c1d2a258599b Anjelique Melendez 2023-11-29 1025 /* 05c1d2a258599b Anjelique Melendez 2023-11-29 1026 * Find "low pause" and "high pause" in the pattern in the LUT case. 05c1d2a258599b Anjelique Melendez 2023-11-29 1027 * SDAM-based devices require equal duration of all steps 05c1d2a258599b Anjelique Melendez 2023-11-29 1028 */ 05c1d2a258599b Anjelique Melendez 2023-11-29 1029 if (lpg->lut_base) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1030 lo_pause = pattern[0].delta_t; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1031 hi_pause = pattern[actual_len - 1].delta_t; 05c1d2a258599b Anjelique Melendez 2023-11-29 1032 } else { 05c1d2a258599b Anjelique Melendez 2023-11-29 1033 if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t) 05c1d2a258599b Anjelique Melendez 2023-11-29 1034 goto out_free_pattern; lo_pause and hi_pause are not set on this else path 05c1d2a258599b Anjelique Melendez 2023-11-29 1035 } 05c1d2a258599b Anjelique Melendez 2023-11-29 1036 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1037 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1038 mutex_lock(&lpg->lock); 05c1d2a258599b Anjelique Melendez 2023-11-29 1039 05c1d2a258599b Anjelique Melendez 2023-11-29 1040 if (lpg->lut_base) 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1041 ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); 05c1d2a258599b Anjelique Melendez 2023-11-29 1042 else 05c1d2a258599b Anjelique Melendez 2023-11-29 1043 ret = lpg_lut_store_sdam(lpg, pattern, actual_len, &lo_idx, &hi_idx); 05c1d2a258599b Anjelique Melendez 2023-11-29 1044 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1045 if (ret < 0) 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1046 goto out_unlock; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1047 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1048 for (i = 0; i < led->num_channels; i++) { 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1049 chan = led->channels[i]; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1050 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1051 chan->ramp_tick_ms = delta_t; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1052 chan->ramp_ping_pong = ping_pong; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1053 chan->ramp_oneshot = repeat != -1; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1054 24e2d05d1b6898 Bjorn Andersson 2022-03-03 @1055 chan->ramp_lo_pause_ms = lo_pause; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 @1056 chan->ramp_hi_pause_ms = hi_pause; ^^^^^^^^ 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1057 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1058 chan->pattern_lo_idx = lo_idx; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1059 chan->pattern_hi_idx = hi_idx; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1060 } 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1061 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1062 out_unlock: 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1063 mutex_unlock(&lpg->lock); e98a860f65428a Bjorn Andersson 2022-05-23 1064 out_free_pattern: e98a860f65428a Bjorn Andersson 2022-05-23 1065 kfree(pattern); 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1066 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1067 return ret; 24e2d05d1b6898 Bjorn Andersson 2022-03-03 1068 }
On Thu, 30 Nov 2023, Dan Carpenter wrote: > Hi Anjelique, > > kernel test robot noticed the following build warnings: > > https://git-scm.com/docs/git-format-patch#_base_tree_information] > > url: https://github.com/intel-lab-lkp/linux/commits/Anjelique-Melendez/dt-bindings-soc-qcom-Add-qcom-pbs-bindings/20231130-094701 > base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next > patch link: https://lore.kernel.org/r/20231130013615.14287-5-quic_amelende%40quicinc.com > patch subject: [PATCH v7 4/7] leds: rgb: leds-qcom-lpg: Add support for PPG through single SDAM > config: csky-randconfig-r071-20231130 (https://download.01.org/0day-ci/archive/20231130/202311302200.RcTP9m0Y-lkp@intel.com/config) > compiler: csky-linux-gcc (GCC) 13.2.0 > reproduce: (https://download.01.org/0day-ci/archive/20231130/202311302200.RcTP9m0Y-lkp@intel.com/reproduce) > > If you fix the issue in a separate patch/commit (i.e. not just a new version of > the same patch/commit), kindly add following tags > | Reported-by: kernel test robot <lkp@intel.com> > | Reported-by: Dan Carpenter <error27@gmail.com> > | Closes: https://lore.kernel.org/r/202311302200.RcTP9m0Y-lkp@intel.com/ > > smatch warnings: > drivers/leds/rgb/leds-qcom-lpg.c:1055 lpg_pattern_set() error: uninitialized symbol 'lo_pause'. > drivers/leds/rgb/leds-qcom-lpg.c:1056 lpg_pattern_set() error: uninitialized symbol 'hi_pause'. FYI, I'll not review this set until these are fixed.
diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 68d82a682bf6..a3e4adffb8df 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -8,11 +8,13 @@ #include <linux/bitfield.h> #include <linux/led-class-multicolor.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/soc/qcom/qcom-pbs.h> #define LPG_SUBTYPE_REG 0x05 #define LPG_SUBTYPE_LPG 0x2 @@ -39,6 +41,8 @@ #define PWM_SEC_ACCESS_REG 0xd0 #define PWM_DTEST_REG(x) (0xe2 + (x) - 1) +#define SDAM_REG_PBS_SEQ_EN 0x42 + #define TRI_LED_SRC_SEL 0x45 #define TRI_LED_EN_CTL 0x46 #define TRI_LED_ATC_CTL 0x47 @@ -48,9 +52,25 @@ #define LPG_RESOLUTION_9BIT BIT(9) #define LPG_RESOLUTION_15BIT BIT(15) +#define PPG_MAX_LED_BRIGHTNESS 255 + #define LPG_MAX_M 7 #define LPG_MAX_PREDIV 6 +#define DEFAULT_TICK_DURATION_US 7800 +#define RAMP_STEP_DURATION(x) (((x) * 1000 / DEFAULT_TICK_DURATION_US) & 0xff) + +/* LPG common config settings for PPG */ +#define SDAM_REG_RAMP_STEP_DURATION 0x47 +#define SDAM_LPG_SDAM_LUT_PATTERN_OFFSET 0x80 + +/* LPG per channel config settings for PPG */ +#define SDAM_LUT_EN_OFFSET 0x0 +#define SDAM_PATTERN_CONFIG_OFFSET 0x1 +#define SDAM_END_INDEX_OFFSET 0x3 +#define SDAM_START_INDEX_OFFSET 0x4 +#define SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET 0x6 + struct lpg_channel; struct lpg_data; @@ -64,6 +84,9 @@ struct lpg_data; * @lut_base: base address of the LUT block (optional) * @lut_size: number of entries in the LUT block * @lut_bitmap: allocation bitmap for LUT entries + * @pbs_dev: PBS device + * @lpg_chan_sdam: LPG SDAM peripheral device + * @pbs_en_bitmap: bitmap for tracking PBS triggers * @triled_base: base address of the TRILED block (optional) * @triled_src: power-source for the TRILED * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register @@ -85,6 +108,10 @@ struct lpg { u32 lut_size; unsigned long *lut_bitmap; + struct pbs_dev *pbs_dev; + struct nvmem_device *lpg_chan_sdam; + unsigned long pbs_en_bitmap; + u32 triled_base; u32 triled_src; bool triled_has_atc_ctl; @@ -101,6 +128,7 @@ struct lpg { * @triled_mask: mask in TRILED to enable this channel * @lut_mask: mask in LUT to start pattern generator for this channel * @subtype: PMIC hardware block subtype + * @sdam_offset: channel offset in LPG SDAM * @in_use: channel is exposed to LED framework * @color: color of the LED attached to this channel * @dtest_line: DTEST line for output, or 0 if disabled @@ -129,6 +157,7 @@ struct lpg_channel { unsigned int triled_mask; unsigned int lut_mask; unsigned int subtype; + u32 sdam_offset; bool in_use; @@ -178,10 +207,12 @@ struct lpg_led { /** * struct lpg_channel_data - per channel initialization data + * @sdam_offset: Channel offset in LPG SDAM * @base: base address for PWM channel registers * @triled_mask: bitmask for controlling this channel in TRILED */ struct lpg_channel_data { + unsigned int sdam_offset; unsigned int base; u8 triled_mask; }; @@ -206,6 +237,52 @@ struct lpg_data { const struct lpg_channel_data *channels; }; +#define PBS_SW_TRIG_BIT BIT(0) + +static int lpg_clear_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) +{ + u8 val = 0; + int rc; + + lpg->pbs_en_bitmap &= (~lut_mask); + if (!lpg->pbs_en_bitmap) { + rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); + if (rc < 0) + return rc; + } + + return 0; +} + +static int lpg_set_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) +{ + u8 val = PBS_SW_TRIG_BIT; + int rc; + + if (!lpg->pbs_en_bitmap) { + rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); + if (rc < 0) + return rc; + + rc = qcom_pbs_trigger_event(lpg->pbs_dev, val); + if (rc < 0) + return rc; + } + lpg->pbs_en_bitmap |= lut_mask; + + return 0; +} + +static int lpg_sdam_configure_triggers(struct lpg_channel *chan, u8 set_trig) +{ + u32 addr = SDAM_LUT_EN_OFFSET + chan->sdam_offset; + + if (!chan->lpg->lpg_chan_sdam) + return 0; + + return nvmem_device_write(chan->lpg->lpg_chan_sdam, addr, 1, &set_trig); +} + static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable) { /* Skip if we don't have a triled block */ @@ -216,6 +293,40 @@ static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable) mask, enable); } +static int lpg_lut_store_sdam(struct lpg *lpg, struct led_pattern *pattern, + size_t len, unsigned int *lo_idx, unsigned int *hi_idx) +{ + unsigned int idx; + u8 brightness; + int i, rc; + u16 addr; + + if (len > lpg->lut_size) { + dev_err(lpg->dev, "Pattern length (%zu) exceeds maximum pattern length (%d)\n", + len, lpg->lut_size); + return -EINVAL; + } + + idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, 0, len, 0); + if (idx >= lpg->lut_size) + return -ENOSPC; + + for (i = 0; i < len; i++) { + brightness = pattern[i].brightness; + addr = SDAM_LPG_SDAM_LUT_PATTERN_OFFSET + i + idx; + rc = nvmem_device_write(lpg->lpg_chan_sdam, addr, 1, &brightness); + if (rc < 0) + return rc; + } + + bitmap_set(lpg->lut_bitmap, idx, len); + + *lo_idx = idx; + *hi_idx = idx + len - 1; + + return 0; +} + static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern, size_t len, unsigned int *lo_idx, unsigned int *hi_idx) { @@ -256,6 +367,9 @@ static void lpg_lut_free(struct lpg *lpg, unsigned int lo_idx, unsigned int hi_i static int lpg_lut_sync(struct lpg *lpg, unsigned int mask) { + if (!lpg->lut_base) + return 0; + return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask); } @@ -462,6 +576,28 @@ static void lpg_apply_pwm_value(struct lpg_channel *chan) #define LPG_PATTERN_CONFIG_PAUSE_HI BIT(1) #define LPG_PATTERN_CONFIG_PAUSE_LO BIT(0) +static void lpg_sdam_apply_lut_control(struct lpg_channel *chan) +{ + struct nvmem_device *lpg_chan_sdam = chan->lpg->lpg_chan_sdam; + unsigned int lo_idx = chan->pattern_lo_idx; + unsigned int hi_idx = chan->pattern_hi_idx; + u8 val = 0, conf = 0; + + if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) + return; + + if (!chan->ramp_oneshot) + conf |= LPG_PATTERN_CONFIG_REPEAT; + + nvmem_device_write(lpg_chan_sdam, SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val); + nvmem_device_write(lpg_chan_sdam, SDAM_PATTERN_CONFIG_OFFSET + chan->sdam_offset, 1, &conf); + nvmem_device_write(lpg_chan_sdam, SDAM_END_INDEX_OFFSET + chan->sdam_offset, 1, &hi_idx); + nvmem_device_write(lpg_chan_sdam, SDAM_START_INDEX_OFFSET + chan->sdam_offset, 1, &lo_idx); + + val = RAMP_STEP_DURATION(chan->ramp_tick_ms); + nvmem_device_write(lpg_chan_sdam, SDAM_REG_RAMP_STEP_DURATION, 1, &val); +} + static void lpg_apply_lut_control(struct lpg_channel *chan) { struct lpg *lpg = chan->lpg; @@ -597,7 +733,10 @@ static void lpg_apply(struct lpg_channel *chan) lpg_apply_pwm_value(chan); lpg_apply_control(chan); lpg_apply_sync(chan); - lpg_apply_lut_control(chan); + if (chan->lpg->lpg_chan_sdam) + lpg_sdam_apply_lut_control(chan); + else + lpg_apply_lut_control(chan); lpg_enable_glitch(chan); } @@ -622,6 +761,7 @@ static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, chan->ramp_enabled = false; } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { lpg_calc_freq(chan, NSEC_PER_MSEC); + lpg_sdam_configure_triggers(chan, 1); chan->enabled = true; chan->ramp_enabled = true; @@ -649,8 +789,10 @@ static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, triled_set(lpg, triled_mask, triled_enabled); /* Trigger start of ramp generator(s) */ - if (lut_mask) + if (lut_mask) { lpg_lut_sync(lpg, lut_mask); + lpg_set_pbs_trigger(lpg, lut_mask); + } } static int lpg_brightness_single_set(struct led_classdev *cdev, @@ -836,18 +978,23 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, * If the specified pattern is a palindrome the ping pong mode is * enabled. In this scenario the delta_t of the middle entry (i.e. the * last in the programmed pattern) determines the "high pause". + * + * SDAM-based devices do not support "ping-pong", "low pause" or "high pause" */ /* Detect palindromes and use "ping pong" to reduce LUT usage */ - for (i = 0; i < len / 2; i++) { - brightness_a = pattern[i].brightness; - brightness_b = pattern[len - i - 1].brightness; - - if (brightness_a != brightness_b) { - ping_pong = false; - break; + if (lpg->lut_base) { + for (i = 0; i < len / 2; i++) { + brightness_a = pattern[i].brightness; + brightness_b = pattern[len - i - 1].brightness; + + if (brightness_a != brightness_b) { + ping_pong = false; + break; + } } - } + } else + ping_pong = false; /* The pattern length to be written to the LUT */ if (ping_pong) @@ -875,12 +1022,26 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, if (delta_t >= BIT(9)) goto out_free_pattern; - /* Find "low pause" and "high pause" in the pattern */ - lo_pause = pattern[0].delta_t; - hi_pause = pattern[actual_len - 1].delta_t; + /* + * Find "low pause" and "high pause" in the pattern in the LUT case. + * SDAM-based devices require equal duration of all steps + */ + if (lpg->lut_base) { + lo_pause = pattern[0].delta_t; + hi_pause = pattern[actual_len - 1].delta_t; + } else { + if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t) + goto out_free_pattern; + } + mutex_lock(&lpg->lock); - ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); + + if (lpg->lut_base) + ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); + else + ret = lpg_lut_store_sdam(lpg, pattern, actual_len, &lo_idx, &hi_idx); + if (ret < 0) goto out_unlock; @@ -928,7 +1089,12 @@ static int lpg_pattern_mc_set(struct led_classdev *cdev, { struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); - int ret; + unsigned int triled_mask = 0; + int ret, i; + + for (i = 0; i < led->num_channels; i++) + triled_mask |= led->channels[i]->triled_mask; + triled_set(led->lpg, triled_mask, 0); ret = lpg_pattern_set(led, pattern, len, repeat); if (ret < 0) @@ -953,6 +1119,8 @@ static int lpg_pattern_clear(struct lpg_led *led) for (i = 0; i < led->num_channels; i++) { chan = led->channels[i]; + lpg_sdam_configure_triggers(chan, 0); + lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask); chan->pattern_lo_idx = 0; chan->pattern_hi_idx = 0; } @@ -1187,8 +1355,8 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) cdev->brightness_set_blocking = lpg_brightness_mc_set; cdev->blink_set = lpg_blink_mc_set; - /* Register pattern accessors only if we have a LUT block */ - if (lpg->lut_base) { + /* Register pattern accessors if we have a LUT block or when using PPG */ + if (lpg->lut_base || lpg->lpg_chan_sdam) { cdev->pattern_set = lpg_pattern_mc_set; cdev->pattern_clear = lpg_pattern_mc_clear; } @@ -1201,15 +1369,19 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) cdev->brightness_set_blocking = lpg_brightness_single_set; cdev->blink_set = lpg_blink_single_set; - /* Register pattern accessors only if we have a LUT block */ - if (lpg->lut_base) { + /* Register pattern accessors if we have a LUT block or when using PPG */ + if (lpg->lut_base || lpg->lpg_chan_sdam) { cdev->pattern_set = lpg_pattern_single_set; cdev->pattern_clear = lpg_pattern_single_clear; } } cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); - cdev->max_brightness = LPG_RESOLUTION_9BIT - 1; + + if (lpg->lpg_chan_sdam) + cdev->max_brightness = PPG_MAX_LED_BRIGHTNESS; + else + cdev->max_brightness = LPG_RESOLUTION_9BIT - 1; if (!of_property_read_string(np, "default-state", &state) && !strcmp(state, "on")) @@ -1250,6 +1422,7 @@ static int lpg_init_channels(struct lpg *lpg) chan->base = data->channels[i].base; chan->triled_mask = data->channels[i].triled_mask; chan->lut_mask = BIT(i); + chan->sdam_offset = data->channels[i].sdam_offset; regmap_read(lpg->map, chan->base + LPG_SUBTYPE_REG, &chan->subtype); } @@ -1296,11 +1469,12 @@ static int lpg_init_lut(struct lpg *lpg) { const struct lpg_data *data = lpg->data; - if (!data->lut_base) + if (!data->lut_size) return 0; - lpg->lut_base = data->lut_base; lpg->lut_size = data->lut_size; + if (data->lut_base) + lpg->lut_base = data->lut_base; lpg->lut_bitmap = devm_bitmap_zalloc(lpg->dev, lpg->lut_size, GFP_KERNEL); if (!lpg->lut_bitmap) @@ -1309,6 +1483,48 @@ static int lpg_init_lut(struct lpg *lpg) return 0; } +static int lpg_init_sdam(struct lpg *lpg) +{ + int i, sdam_count, rc; + u8 val = 0; + + sdam_count = of_property_count_strings(lpg->dev->of_node, "nvmem-names"); + if (sdam_count <= 0) + return 0; + + /* Get the SDAM device for LPG/LUT config */ + lpg->lpg_chan_sdam = devm_nvmem_device_get(lpg->dev, "lpg_chan_sdam"); + if (IS_ERR(lpg->lpg_chan_sdam)) + return dev_err_probe(lpg->dev, PTR_ERR(lpg->lpg_chan_sdam), + "Failed to get LPG chan SDAM device\n"); + + lpg->pbs_dev = get_pbs_client_device(lpg->dev); + if (IS_ERR(lpg->pbs_dev)) + return dev_err_probe(lpg->dev, PTR_ERR(lpg->pbs_dev), + "Failed to get PBS client device\n"); + + for (i = 0; i < lpg->num_channels; i++) { + struct lpg_channel *chan = &lpg->channels[i]; + + if (chan->sdam_offset) { + rc = nvmem_device_write(lpg->lpg_chan_sdam, + SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val); + if (rc < 0) + return rc; + + rc = lpg_sdam_configure_triggers(chan, 0); + if (rc < 0) + return rc; + + rc = lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask); + if (rc < 0) + return rc; + } + } + + return 0; +} + static int lpg_probe(struct platform_device *pdev) { struct device_node *np; @@ -1345,6 +1561,10 @@ static int lpg_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = lpg_init_sdam(lpg); + if (ret < 0) + return ret; + ret = lpg_init_lut(lpg); if (ret < 0) return ret;