1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863 |
- /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/regmap.h>
- #include <linux/errno.h>
- #include <linux/leds.h>
- #include <linux/slab.h>
- #include <linux/of_device.h>
- #include <linux/of_address.h>
- #include <linux/spmi.h>
- #include <linux/platform_device.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/err.h>
- #include <linux/delay.h>
- #include <linux/leds-qpnp-wled.h>
- #include <linux/qpnp/qpnp-revid.h>
- /* base addresses */
- #define QPNP_WLED_CTRL_BASE "qpnp-wled-ctrl-base"
- #define QPNP_WLED_SINK_BASE "qpnp-wled-sink-base"
- /* ctrl registers */
- #define QPNP_WLED_FAULT_STATUS(b) (b + 0x08)
- #define QPNP_WLED_INT_RT_STS(b) (b + 0x10)
- #define QPNP_WLED_EXT_PIN_CTL(b) (b + 0x45)
- #define QPNP_WLED_EN_REG(b) (b + 0x46)
- #define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48)
- #define QPNP_WLED_VREF_REG(b) (b + 0x49)
- #define QPNP_WLED_BOOST_DUTY_REG(b) (b + 0x4B)
- #define QPNP_WLED_SWITCH_FREQ_REG(b) (b + 0x4C)
- #define QPNP_WLED_OVP_REG(b) (b + 0x4D)
- #define QPNP_WLED_ILIM_REG(b) (b + 0x4E)
- #define QPNP_WLED_AMOLED_VOUT_REG(b) (b + 0x4F)
- #define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
- #define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
- #define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
- #define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A)
- #define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
- #define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C)
- #define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
- #define QPNP_WLED_SWIRE_AVDD_REG(b) (b + 0x5F)
- #define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF)
- #define QPNP_WLED_TEST1_REG(b) (b + 0xE2)
- #define QPNP_WLED_TEST4_REG(b) (b + 0xE5)
- #define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2)
- #define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0)
- #define QPNP_WLED_EN_MASK 0x7F
- #define QPNP_WLED_EN_SHIFT 7
- #define QPNP_WLED_FDBK_OP_MASK 0xF8
- #define QPNP_WLED_VREF_MASK GENMASK(3, 0)
- #define QPNP_WLED_VLOOP_COMP_RES_MASK 0xF0
- #define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE 0x80
- #define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM 20
- #define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM 20
- #define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM 320
- #define QPNP_WLED_VLOOP_COMP_GM_MASK GENMASK(3, 0)
- #define QPNP_WLED_VLOOP_COMP_GM_OVERWRITE 0x80
- #define QPNP_WLED_VLOOP_COMP_AUTO_GM_EN BIT(6)
- #define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK GENMASK(5, 4)
- #define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT 4
- #define QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994 0x03
- #define QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998 0x09
- #define QPNP_WLED_LOOP_GM_DFLT_WLED 0x09
- #define QPNP_WLED_LOOP_EA_GM_MIN 0x0
- #define QPNP_WLED_LOOP_EA_GM_MAX 0xF
- #define QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX 3
- #define QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH 1
- #define QPNP_WLED_VREF_PSM_MASK 0xF8
- #define QPNP_WLED_VREF_PSM_STEP_MV 50
- #define QPNP_WLED_VREF_PSM_MIN_MV 400
- #define QPNP_WLED_VREF_PSM_MAX_MV 750
- #define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
- #define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7)
- #define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1
- #define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF
- #define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7
- #define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7)
- #define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0)
- #define QPNP_WLED_EN_PSM_BIT BIT(7)
- #define QPNP_WLED_EXT_PIN_CTL_BIT BIT(7)
- #define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
- #define QPNP_WLED_ILIM_OVERWRITE BIT(7)
- #define PMI8994_WLED_ILIM_MIN_MA 105
- #define PMI8994_WLED_ILIM_MAX_MA 1980
- #define PMI8994_WLED_DFLT_ILIM_MA 980
- #define PMI8994_AMOLED_DFLT_ILIM_MA 385
- #define PMI8998_WLED_ILIM_MAX_MA 1500
- #define PMI8998_WLED_DFLT_ILIM_MA 970
- #define PMI8998_AMOLED_DFLT_ILIM_MA 620
- #define QPNP_WLED_BOOST_DUTY_MASK 0xFC
- #define QPNP_WLED_BOOST_DUTY_STEP_NS 52
- #define QPNP_WLED_BOOST_DUTY_MIN_NS 26
- #define QPNP_WLED_BOOST_DUTY_MAX_NS 156
- #define QPNP_WLED_DEF_BOOST_DUTY_NS 104
- #define QPNP_WLED_SWITCH_FREQ_MASK GENMASK(3, 0)
- #define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
- #define QPNP_WLED_OVP_MASK GENMASK(1, 0)
- #define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT BIT(6)
- #define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT BIT(5)
- #define QPNP_WLED_TEST4_EN_CLAMP_BIT BIT(4)
- #define QPNP_WLED_TEST4_EN_SOFT_START_BIT BIT(1)
- #define QPNP_WLED_TEST4_EN_VREF_UP \
- (QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT | \
- QPNP_WLED_TEST4_EN_CLAMP_BIT | \
- QPNP_WLED_TEST4_EN_SOFT_START_BIT)
- #define QPNP_WLED_TEST4_EN_IIND_UP 0x1
- #define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
- #define QPNP_WLED_OVP_FAULT_BIT BIT(1)
- #define QPNP_WLED_SC_FAULT_BIT BIT(2)
- #define QPNP_WLED_OVP_FLT_RT_STS_BIT BIT(1)
- /* QPNP_WLED_SOFTSTART_RAMP_DLY */
- #define SOFTSTART_OVERWRITE_BIT BIT(7)
- #define SOFTSTART_RAMP_DELAY_MASK GENMASK(2, 0)
- /* sink registers */
- #define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
- #define QPNP_WLED_SYNC_REG(b) (b + 0x47)
- #define QPNP_WLED_MOD_REG(b) (b + 0x4A)
- #define QPNP_WLED_HYB_THRES_REG(b) (b + 0x4B)
- #define QPNP_WLED_MOD_EN_REG(b, n) (b + 0x50 + (n * 0x10))
- #define QPNP_WLED_SYNC_DLY_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x01)
- #define QPNP_WLED_FS_CURR_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x02)
- #define QPNP_WLED_CABC_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x06)
- #define QPNP_WLED_BRIGHT_LSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x07)
- #define QPNP_WLED_BRIGHT_MSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x08)
- #define QPNP_WLED_SINK_TEST5_REG(b) (b + 0xE6)
- #define QPNP_WLED_MOD_FREQ_1200_KHZ 1200
- #define QPNP_WLED_MOD_FREQ_2400_KHZ 2400
- #define QPNP_WLED_MOD_FREQ_9600_KHZ 9600
- #define QPNP_WLED_MOD_FREQ_19200_KHZ 19200
- #define QPNP_WLED_MOD_FREQ_MASK 0x3F
- #define QPNP_WLED_MOD_FREQ_SHIFT 6
- #define QPNP_WLED_ACC_CLK_FREQ_MASK 0xE7
- #define QPNP_WLED_ACC_CLK_FREQ_SHIFT 3
- #define QPNP_WLED_PHASE_STAG_MASK 0xDF
- #define QPNP_WLED_PHASE_STAG_SHIFT 5
- #define QPNP_WLED_DIM_RES_MASK 0xFD
- #define QPNP_WLED_DIM_RES_SHIFT 1
- #define QPNP_WLED_DIM_HYB_MASK 0xFB
- #define QPNP_WLED_DIM_HYB_SHIFT 2
- #define QPNP_WLED_DIM_ANA_MASK 0xFE
- #define QPNP_WLED_HYB_THRES_MASK 0xF8
- #define QPNP_WLED_HYB_THRES_MIN 78
- #define QPNP_WLED_DEF_HYB_THRES 625
- #define QPNP_WLED_HYB_THRES_MAX 10000
- #define QPNP_WLED_MOD_EN_MASK 0x7F
- #define QPNP_WLED_MOD_EN_SHFT 7
- #define QPNP_WLED_MOD_EN 1
- #define QPNP_WLED_GATE_DRV_MASK 0xFE
- #define QPNP_WLED_SYNC_DLY_MASK GENMASK(2, 0)
- #define QPNP_WLED_SYNC_DLY_MIN_US 0
- #define QPNP_WLED_SYNC_DLY_MAX_US 1400
- #define QPNP_WLED_SYNC_DLY_STEP_US 200
- #define QPNP_WLED_DEF_SYNC_DLY_US 400
- #define QPNP_WLED_FS_CURR_MASK GENMASK(3, 0)
- #define QPNP_WLED_FS_CURR_MIN_UA 0
- #define QPNP_WLED_FS_CURR_MAX_UA 30000
- #define QPNP_WLED_FS_CURR_STEP_UA 2500
- #define QPNP_WLED_CABC_MASK 0x80
- #define QPNP_WLED_CABC_SHIFT 7
- #define QPNP_WLED_CURR_SINK_SHIFT 4
- #define QPNP_WLED_CURR_SINK_MASK GENMASK(7, 4)
- #define QPNP_WLED_BRIGHT_LSB_MASK 0xFF
- #define QPNP_WLED_BRIGHT_MSB_SHIFT 8
- #define QPNP_WLED_BRIGHT_MSB_MASK 0x0F
- #define QPNP_WLED_SYNC 0x0F
- #define QPNP_WLED_SYNC_RESET 0x00
- #define QPNP_WLED_SINK_TEST5_HYB 0x14
- #define QPNP_WLED_SINK_TEST5_DIG 0x1E
- #define QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT BIT(3)
- #define QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE 0x0B
- #define QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE 0x05
- #define QPNP_WLED_DISP_SEL_REG(b) (b + 0x44)
- #define QPNP_WLED_MODULE_RDY_REG(b) (b + 0x45)
- #define QPNP_WLED_MODULE_EN_REG(b) (b + 0x46)
- #define QPNP_WLED_MODULE_RDY_MASK 0x7F
- #define QPNP_WLED_MODULE_RDY_SHIFT 7
- #define QPNP_WLED_MODULE_EN_MASK BIT(7)
- #define QPNP_WLED_MODULE_EN_SHIFT 7
- #define QPNP_WLED_DISP_SEL_MASK 0x7F
- #define QPNP_WLED_DISP_SEL_SHIFT 7
- #define QPNP_WLED_EN_SC_DEB_CYCLES_MASK 0x79
- #define QPNP_WLED_EN_DEB_CYCLES_MASK 0xF9
- #define QPNP_WLED_EN_SC_SHIFT 7
- #define QPNP_WLED_SC_PRO_EN_DSCHGR 0x8
- #define QPNP_WLED_SC_DEB_CYCLES_MIN 2
- #define QPNP_WLED_SC_DEB_CYCLES_MAX 16
- #define QPNP_WLED_SC_DEB_CYCLES_SUB 2
- #define QPNP_WLED_SC_DEB_CYCLES_DFLT 4
- #define QPNP_WLED_EXT_FET_DTEST2 0x09
- #define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0)
- #define QPNP_WLED_SEC_UNLOCK 0xA5
- #define NUM_DDIC_CODES 256
- #define QPNP_WLED_MAX_STRINGS 4
- #define QPNP_PM660_WLED_MAX_STRINGS 3
- #define WLED_MAX_LEVEL_4095 4095
- #define QPNP_WLED_RAMP_DLY_MS 20
- #define QPNP_WLED_TRIGGER_NONE "none"
- #define QPNP_WLED_STR_SIZE 20
- #define QPNP_WLED_MIN_MSLEEP 20
- #define QPNP_WLED_SC_DLY_MS 20
- #define QPNP_WLED_SOFT_START_DLY_US 10000
- #define NUM_SUPPORTED_AVDD_VOLTAGES 6
- #define QPNP_WLED_DFLT_AVDD_MV 7600
- #define QPNP_WLED_AVDD_MIN_MV 5650
- #define QPNP_WLED_AVDD_MAX_MV 7900
- #define QPNP_WLED_AVDD_STEP_MV 150
- #define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0
- #define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF
- #define QPNP_WLED_AVDD_SEL_SPMI_BIT BIT(7)
- #define QPNP_WLED_AVDD_SET_BIT BIT(4)
- #define NUM_SUPPORTED_OVP_THRESHOLDS 4
- #define NUM_SUPPORTED_ILIM_THRESHOLDS 8
- #define QPNP_WLED_AVDD_MV_TO_REG(val) \
- ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
- /* output feedback mode */
- enum qpnp_wled_fdbk_op {
- QPNP_WLED_FDBK_AUTO,
- QPNP_WLED_FDBK_WLED1,
- QPNP_WLED_FDBK_WLED2,
- QPNP_WLED_FDBK_WLED3,
- QPNP_WLED_FDBK_WLED4,
- };
- /* dimming modes */
- enum qpnp_wled_dim_mode {
- QPNP_WLED_DIM_ANALOG,
- QPNP_WLED_DIM_DIGITAL,
- QPNP_WLED_DIM_HYBRID,
- };
- /* wled ctrl debug registers */
- static u8 qpnp_wled_ctrl_dbg_regs[] = {
- 0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53,
- 0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e, 0xe2
- };
- /* wled sink debug registers */
- static u8 qpnp_wled_sink_dbg_regs[] = {
- 0x46, 0x47, 0x48, 0x4a, 0x4b,
- 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x58,
- 0x60, 0x61, 0x62, 0x63, 0x66, 0x67, 0x68,
- 0x70, 0x71, 0x72, 0x73, 0x76, 0x77, 0x78,
- 0x80, 0x81, 0x82, 0x83, 0x86, 0x87, 0x88,
- 0xe6,
- };
- static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
- 7900, 7600, 7300, 6400, 6100, 5800,
- };
- static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
- 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
- };
- static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
- 3, 0, -2, 7, 3, 3,
- };
- static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
- 31000, 29500, 19400, 17800,
- };
- static int qpnp_wled_ovp_thresholds_pmi8998[NUM_SUPPORTED_OVP_THRESHOLDS] = {
- 31100, 29600, 19600, 18100,
- };
- static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
- 105, 385, 660, 980, 1150, 1420, 1700, 1980,
- };
- static int qpnp_wled_ilim_settings_pmi8998[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
- 105, 280, 450, 620, 970, 1150, 1300, 1500,
- };
- struct wled_vref_setting {
- u32 min_uv;
- u32 max_uv;
- u32 step_uv;
- u32 default_uv;
- };
- static struct wled_vref_setting vref_setting_pmi8994 = {
- 300000, 675000, 25000, 350000,
- };
- static struct wled_vref_setting vref_setting_pmi8998 = {
- 60000, 397500, 22500, 127500,
- };
- /**
- * qpnp_wled - wed data structure
- * @ cdev - led class device
- * @ pdev - platform device
- * @ work - worker for led operation
- * @ wq - workqueue for setting brightness level
- * @ lock - mutex lock for exclusive access
- * @ fdbk_op - output feedback mode
- * @ dim_mode - dimming mode
- * @ ovp_irq - over voltage protection irq
- * @ sc_irq - short circuit irq
- * @ sc_cnt - short circuit irq count
- * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
- * @ ctrl_base - base address for wled ctrl
- * @ sink_base - base address for wled sink
- * @ mod_freq_khz - modulator frequency in KHZ
- * @ hyb_thres - threshold for hybrid dimming
- * @ sync_dly_us - sync delay in us
- * @ vref_uv - ref voltage in uv
- * @ vref_psm_mv - ref psm voltage in mv
- * @ loop_comp_res_kohm - control to select the compensation resistor
- * @ loop_ea_gm - control to select the gm for the gm stage in control loop
- * @ sc_deb_cycles - debounce time for short circuit detection
- * @ switch_freq_khz - switching frequency in KHZ
- * @ ovp_mv - over voltage protection in mv
- * @ ilim_ma - current limiter in ma
- * @ boost_duty_ns - boost duty cycle in ns
- * @ fs_curr_ua - full scale current in ua
- * @ ramp_ms - delay between ramp steps in ms
- * @ ramp_step - ramp step size
- * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
- * @ auto_calibration_ovp_count - OVP fault irq count to run auto calibration
- * @ max_strings - Number of strings supported in WLED peripheral
- * @ prev_level - Previous brightness level
- * @ brt_map_table - Brightness map table
- * @ strings - supported list of strings
- * @ num_strings - number of strings
- * @ loop_auto_gm_thresh - the clamping level for auto gm
- * @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
- * @ loop_auto_gm_en - select if auto gm is enabled
- * @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
- * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
- * @ avdd_mode_spmi - enable avdd programming via spmi
- * @ en_9b_dim_res - enable or disable 9bit dimming
- * @ en_phase_stag - enable or disable phase staggering
- * @ en_cabc - enable or disable cabc
- * @ disp_type_amoled - type of display: LCD/AMOLED
- * @ en_ext_pfet_sc_pro - enable sc protection on external pfet
- * @ prev_state - previous state of WLED
- * @ stepper_en - Flag to enable stepper algorithm
- * @ ovp_irq_disabled - OVP interrupt disable status
- * @ auto_calib_enabled - Flag to enable auto calibration feature
- * @ auto_calib_done - Flag to indicate auto calibration is done
- * @ module_dis_perm - Flat to keep module permanently disabled
- * @ start_ovp_fault_time - Time when the OVP fault first occurred
- */
- struct qpnp_wled {
- struct led_classdev cdev;
- struct platform_device *pdev;
- struct regmap *regmap;
- struct pmic_revid_data *pmic_rev_id;
- struct work_struct work;
- struct workqueue_struct *wq;
- struct mutex lock;
- struct mutex bus_lock;
- enum qpnp_wled_fdbk_op fdbk_op;
- enum qpnp_wled_dim_mode dim_mode;
- int ovp_irq;
- int sc_irq;
- u32 sc_cnt;
- u32 avdd_target_voltage_mv;
- u16 ctrl_base;
- u16 sink_base;
- u16 mod_freq_khz;
- u16 hyb_thres;
- u16 sync_dly_us;
- u32 vref_uv;
- u16 vref_psm_mv;
- u16 loop_comp_res_kohm;
- u16 loop_ea_gm;
- u16 sc_deb_cycles;
- u16 switch_freq_khz;
- u16 ovp_mv;
- u16 ilim_ma;
- u16 boost_duty_ns;
- u16 fs_curr_ua;
- u16 ramp_ms;
- u16 ramp_step;
- u16 cons_sync_write_delay_us;
- u16 auto_calibration_ovp_count;
- u16 max_strings;
- u16 prev_level;
- u16 *brt_map_table;
- u8 strings[QPNP_WLED_MAX_STRINGS];
- u8 num_strings;
- u8 loop_auto_gm_thresh;
- u8 lcd_auto_pfm_thresh;
- bool loop_auto_gm_en;
- bool lcd_auto_pfm_en;
- bool lcd_psm_ctrl;
- bool avdd_mode_spmi;
- bool en_9b_dim_res;
- bool en_phase_stag;
- bool en_cabc;
- bool disp_type_amoled;
- bool en_ext_pfet_sc_pro;
- bool prev_state;
- bool stepper_en;
- bool ovp_irq_disabled;
- bool secure_mode;
- bool auto_calib_enabled;
- bool auto_calib_done;
- bool module_dis_perm;
- ktime_t start_ovp_fault_time;
- };
- static int qpnp_wled_step_delay_us = 52000;
- module_param_named(
- total_step_delay_us, qpnp_wled_step_delay_us, int, 0600
- );
- static int qpnp_wled_step_size_threshold = 3;
- module_param_named(
- step_size_threshold, qpnp_wled_step_size_threshold, int, 0600
- );
- static int qpnp_wled_step_delay_gain = 2;
- module_param_named(
- step_delay_gain, qpnp_wled_step_delay_gain, int, 0600
- );
- /* helper to read a pmic register */
- static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data)
- {
- int rc;
- uint val;
- rc = regmap_read(wled->regmap, addr, &val);
- if (rc < 0) {
- dev_err(&wled->pdev->dev,
- "Error reading address: %x(%d)\n", addr, rc);
- return rc;
- }
- *data = (u8)val;
- return 0;
- }
- /* helper to write a pmic register */
- static int qpnp_wled_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
- {
- int rc;
- mutex_lock(&wled->bus_lock);
- rc = regmap_write(wled->regmap, addr, data);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
- addr, rc);
- goto out;
- }
- dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
- out:
- mutex_unlock(&wled->bus_lock);
- return rc;
- }
- static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u16 addr,
- u8 mask, u8 data)
- {
- int rc;
- mutex_lock(&wled->bus_lock);
- rc = regmap_update_bits(wled->regmap, addr, mask, data);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
- addr, rc);
- goto out;
- }
- dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
- out:
- mutex_unlock(&wled->bus_lock);
- return rc;
- }
- static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
- {
- int rc;
- u8 reg = QPNP_WLED_SEC_UNLOCK;
- u16 base_addr = addr & 0xFF00;
- mutex_lock(&wled->bus_lock);
- rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
- reg);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
- QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
- goto out;
- }
- rc = regmap_write(wled->regmap, addr, data);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
- addr, rc);
- goto out;
- }
- dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
- out:
- mutex_unlock(&wled->bus_lock);
- return rc;
- }
- static int qpnp_wled_swire_avdd_config(struct qpnp_wled *wled)
- {
- int rc;
- u8 val;
- if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
- wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
- return 0;
- if (!wled->disp_type_amoled || wled->avdd_mode_spmi)
- return 0;
- val = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base), val);
- return rc;
- }
- static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
- {
- int rc;
- u8 reg;
- /* sync */
- reg = QPNP_WLED_SYNC;
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
- reg);
- if (rc < 0)
- return rc;
- if (wled->cons_sync_write_delay_us)
- usleep_range(wled->cons_sync_write_delay_us,
- wled->cons_sync_write_delay_us + 1);
- reg = QPNP_WLED_SYNC_RESET;
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
- reg);
- if (rc < 0)
- return rc;
- return 0;
- }
- /* set wled to a level of brightness */
- static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
- {
- int i, rc;
- u8 reg;
- u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
- /* WLED's lower limit of operation is 0.4% */
- if (level > 0 && level < low_limit)
- level = low_limit;
- /* set brightness registers */
- for (i = 0; i < wled->max_strings; i++) {
- reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc < 0)
- return rc;
- reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
- reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc < 0)
- return rc;
- }
- rc = qpnp_wled_sync_reg_toggle(wled);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
- return rc;
- }
- pr_debug("level:%d\n", level);
- return 0;
- }
- static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level)
- {
- int rc, i;
- if (level < wled->prev_level) {
- for (i = wled->prev_level; i >= level; i--) {
- rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
- if (rc < 0) {
- pr_err("set brightness level failed, rc:%d\n",
- rc);
- return rc;
- }
- }
- } else if (level > wled->prev_level) {
- for (i = wled->prev_level; i <= level; i++) {
- rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
- if (rc < 0) {
- pr_err("set brightness level failed, rc:%d\n",
- rc);
- return rc;
- }
- }
- }
- return 0;
- }
- static int qpnp_wled_set_step_level(struct qpnp_wled *wled, int new_level)
- {
- int rc, i, num_steps, delay_us;
- u16 level, start_level, end_level, step_size;
- bool level_inc = false;
- level = wled->prev_level;
- start_level = wled->brt_map_table[level];
- end_level = wled->brt_map_table[new_level];
- level_inc = (new_level > level);
- num_steps = abs(start_level - end_level);
- if (!num_steps)
- return 0;
- delay_us = qpnp_wled_step_delay_us / num_steps;
- pr_debug("level goes from [%d %d] num_steps: %d, delay: %d\n",
- start_level, end_level, num_steps, delay_us);
- if (delay_us < 500) {
- step_size = 1000 / delay_us;
- num_steps = num_steps / step_size;
- delay_us = 1000;
- } else {
- if (num_steps < qpnp_wled_step_size_threshold)
- delay_us *= qpnp_wled_step_delay_gain;
- step_size = 1;
- }
- i = start_level;
- while (num_steps--) {
- if (level_inc)
- i += step_size;
- else
- i -= step_size;
- rc = qpnp_wled_set_level(wled, i);
- if (rc < 0)
- return rc;
- if (delay_us > 0) {
- if (delay_us < 20000)
- usleep_range(delay_us, delay_us + 1);
- else
- msleep(delay_us / USEC_PER_MSEC);
- }
- }
- if (i != end_level) {
- i = end_level;
- rc = qpnp_wled_set_level(wled, i);
- if (rc < 0)
- return rc;
- }
- return 0;
- }
- static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
- {
- int rc;
- if (!wled->lcd_psm_ctrl)
- return 0;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
- QPNP_WLED_EN_PSM_BIT,
- enable ? QPNP_WLED_EN_PSM_BIT : 0);
- if (rc < 0)
- return rc;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
- QPNP_WLED_PSM_OVERWRITE_BIT,
- enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
- if (rc < 0)
- return rc;
- return 0;
- }
- static int qpnp_wled_module_en(struct qpnp_wled *wled,
- u16 base_addr, bool state)
- {
- int rc;
- if (wled->module_dis_perm)
- return 0;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(base_addr),
- QPNP_WLED_MODULE_EN_MASK,
- state << QPNP_WLED_MODULE_EN_SHIFT);
- if (rc < 0)
- return rc;
- /*
- * Wait for at least 10ms before enabling OVP fault interrupt after
- * enabling the module so that soft start is completed. Also, this
- * delay can be used to control PSM during enable when required. Keep
- * OVP interrupt disabled when the module is disabled.
- */
- if (state) {
- usleep_range(QPNP_WLED_SOFT_START_DLY_US,
- QPNP_WLED_SOFT_START_DLY_US + 1000);
- rc = qpnp_wled_psm_config(wled, false);
- if (rc < 0)
- return rc;
- if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
- enable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = false;
- }
- } else {
- if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
- disable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = true;
- }
- rc = qpnp_wled_psm_config(wled, true);
- if (rc < 0)
- return rc;
- }
- return 0;
- }
- /* sysfs store function for ramp */
- static ssize_t qpnp_wled_ramp_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int i, rc;
- mutex_lock(&wled->lock);
- if (!wled->cdev.brightness) {
- rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled enable failed\n");
- goto unlock_mutex;
- }
- }
- /* ramp up */
- for (i = 0; i <= wled->cdev.max_brightness;) {
- rc = qpnp_wled_set_level(wled, i);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled set level failed\n");
- goto restore_brightness;
- }
- if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
- usleep_range(wled->ramp_ms * USEC_PER_MSEC,
- wled->ramp_ms * USEC_PER_MSEC);
- else
- msleep(wled->ramp_ms);
- if (i == wled->cdev.max_brightness)
- break;
- i += wled->ramp_step;
- if (i > wled->cdev.max_brightness)
- i = wled->cdev.max_brightness;
- }
- /* ramp down */
- for (i = wled->cdev.max_brightness; i >= 0;) {
- rc = qpnp_wled_set_level(wled, i);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled set level failed\n");
- goto restore_brightness;
- }
- if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
- usleep_range(wled->ramp_ms * USEC_PER_MSEC,
- wled->ramp_ms * USEC_PER_MSEC);
- else
- msleep(wled->ramp_ms);
- if (i == 0)
- break;
- i -= wled->ramp_step;
- if (i < 0)
- i = 0;
- }
- dev_info(&wled->pdev->dev, "wled ramp complete\n");
- restore_brightness:
- /* restore the old brightness */
- qpnp_wled_set_level(wled, wled->cdev.brightness);
- if (!wled->cdev.brightness) {
- rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
- if (rc)
- dev_err(&wled->pdev->dev, "wled enable failed\n");
- }
- unlock_mutex:
- mutex_unlock(&wled->lock);
- return count;
- }
- static int qpnp_wled_dump_regs(struct qpnp_wled *wled, u16 base_addr,
- u8 dbg_regs[], u8 size, char *label,
- int count, char *buf)
- {
- int i, rc;
- u8 reg;
- for (i = 0; i < size; i++) {
- rc = qpnp_wled_read_reg(wled, base_addr + dbg_regs[i], ®);
- if (rc < 0)
- return rc;
- count += snprintf(buf + count, PAGE_SIZE - count,
- "%s: REG_0x%x = 0x%x\n", label,
- base_addr + dbg_regs[i], reg);
- if (count >= PAGE_SIZE)
- return PAGE_SIZE - 1;
- }
- return count;
- }
- /* sysfs show function for debug registers */
- static ssize_t qpnp_wled_dump_regs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int count = 0;
- count = qpnp_wled_dump_regs(wled, wled->ctrl_base,
- qpnp_wled_ctrl_dbg_regs,
- ARRAY_SIZE(qpnp_wled_ctrl_dbg_regs),
- "wled_ctrl", count, buf);
- if (count < 0 || count == PAGE_SIZE - 1)
- return count;
- count = qpnp_wled_dump_regs(wled, wled->sink_base,
- qpnp_wled_sink_dbg_regs,
- ARRAY_SIZE(qpnp_wled_sink_dbg_regs),
- "wled_sink", count, buf);
- if (count < 0 || count == PAGE_SIZE - 1)
- return count;
- return count;
- }
- /* sysfs show function for ramp delay in each step */
- static ssize_t qpnp_wled_ramp_ms_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_ms);
- }
- /* sysfs store function for ramp delay in each step */
- static ssize_t qpnp_wled_ramp_ms_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int data, rc;
- rc = kstrtoint(buf, 10, &data);
- if (rc)
- return rc;
- wled->ramp_ms = data;
- return count;
- }
- /* sysfs show function for ramp step */
- static ssize_t qpnp_wled_ramp_step_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_step);
- }
- /* sysfs store function for ramp step */
- static ssize_t qpnp_wled_ramp_step_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int data, rc;
- rc = kstrtoint(buf, 10, &data);
- if (rc)
- return rc;
- wled->ramp_step = data;
- return count;
- }
- /* sysfs function for irqs enable/disable */
- static ssize_t qpnp_wled_irq_control(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int val, rc;
- rc = kstrtouint(buf, 0, &val);
- if (rc < 0)
- return rc;
- if (val != 0 && val != 1)
- return count;
- mutex_lock(&wled->lock);
- /* Disable irqs */
- if (val == 1 && !wled->secure_mode) {
- if (wled->ovp_irq > 0)
- disable_irq(wled->ovp_irq);
- if (wled->sc_irq > 0)
- disable_irq(wled->sc_irq);
- wled->secure_mode = true;
- } else if (val == 0 && wled->secure_mode) {
- if (wled->ovp_irq > 0)
- enable_irq(wled->ovp_irq);
- if (wled->sc_irq > 0)
- enable_irq(wled->sc_irq);
- wled->secure_mode = false;
- }
- mutex_unlock(&wled->lock);
- return count;
- }
- /* sysfs show function for dim mode */
- static ssize_t qpnp_wled_dim_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- char *str;
- if (wled->dim_mode == QPNP_WLED_DIM_ANALOG)
- str = "analog";
- else if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL)
- str = "digital";
- else
- str = "hybrid";
- return snprintf(buf, PAGE_SIZE, "%s\n", str);
- }
- /* sysfs store function for dim mode*/
- static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- char str[QPNP_WLED_STR_SIZE + 1];
- int rc, temp;
- u8 reg;
- if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
- return -EINVAL;
- if (strcmp(str, "analog") == 0)
- temp = QPNP_WLED_DIM_ANALOG;
- else if (strcmp(str, "digital") == 0)
- temp = QPNP_WLED_DIM_DIGITAL;
- else
- temp = QPNP_WLED_DIM_HYBRID;
- if (temp == wled->dim_mode)
- return count;
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), ®);
- if (rc < 0)
- return rc;
- if (temp == QPNP_WLED_DIM_HYBRID) {
- reg &= QPNP_WLED_DIM_HYB_MASK;
- reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
- } else {
- reg &= QPNP_WLED_DIM_HYB_MASK;
- reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
- reg &= QPNP_WLED_DIM_ANA_MASK;
- reg |= temp;
- }
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
- if (rc)
- return rc;
- wled->dim_mode = temp;
- return count;
- }
- /* sysfs show function for full scale current in ua*/
- static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua);
- }
- /* sysfs store function for full scale current in ua*/
- static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct qpnp_wled *wled = dev_get_drvdata(dev);
- int data, i, rc;
- u8 reg;
- rc = kstrtoint(buf, 10, &data);
- if (rc)
- return rc;
- for (i = 0; i < wled->max_strings; i++) {
- if (data < QPNP_WLED_FS_CURR_MIN_UA)
- data = QPNP_WLED_FS_CURR_MIN_UA;
- else if (data > QPNP_WLED_FS_CURR_MAX_UA)
- data = QPNP_WLED_FS_CURR_MAX_UA;
- reg = data / QPNP_WLED_FS_CURR_STEP_UA;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
- QPNP_WLED_FS_CURR_MASK, reg);
- if (rc < 0)
- return rc;
- }
- wled->fs_curr_ua = data;
- rc = qpnp_wled_sync_reg_toggle(wled);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
- return rc;
- }
- return count;
- }
- /* sysfs attributes exported by wled */
- static struct device_attribute qpnp_wled_attrs[] = {
- __ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
- __ATTR(dim_mode, 0664, qpnp_wled_dim_mode_show,
- qpnp_wled_dim_mode_store),
- __ATTR(fs_curr_ua, 0664, qpnp_wled_fs_curr_ua_show,
- qpnp_wled_fs_curr_ua_store),
- __ATTR(start_ramp, 0664, NULL, qpnp_wled_ramp_store),
- __ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store),
- __ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
- qpnp_wled_ramp_step_store),
- __ATTR(secure_mode, 0664, NULL, qpnp_wled_irq_control),
- };
- /* worker for setting wled brightness */
- static void qpnp_wled_work(struct work_struct *work)
- {
- struct qpnp_wled *wled;
- int level, level_255, rc;
- wled = container_of(work, struct qpnp_wled, work);
- mutex_lock(&wled->lock);
- if (wled->secure_mode) {
- pr_debug("Can not set brightness in secure_mode\n ");
- goto unlock_mutex;
- }
- level = wled->cdev.brightness;
- if (wled->brt_map_table) {
- /*
- * Change the 12 bit level to 8 bit level and use the mapped
- * values for 12 bit level from brightness map table.
- */
- level_255 = DIV_ROUND_CLOSEST(level, 16);
- if (level_255 > 255)
- level_255 = 255;
- pr_debug("level: %d level_255: %d\n", level, level_255);
- if (wled->stepper_en)
- rc = qpnp_wled_set_step_level(wled, level_255);
- else
- rc = qpnp_wled_set_map_level(wled, level_255);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled set level failed\n");
- goto unlock_mutex;
- }
- wled->prev_level = level_255;
- } else if (level) {
- rc = qpnp_wled_set_level(wled, level);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled set level failed\n");
- goto unlock_mutex;
- }
- }
- if (!!level != wled->prev_state) {
- if (!!level) {
- /*
- * For AMOLED display in pmi8998, SWIRE_AVDD_DEFAULT has
- * to be reconfigured every time the module is enabled.
- */
- rc = qpnp_wled_swire_avdd_config(wled);
- if (rc < 0) {
- pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
- rc);
- goto unlock_mutex;
- }
- }
- rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
- if (rc) {
- dev_err(&wled->pdev->dev, "wled %sable failed\n",
- level ? "en" : "dis");
- goto unlock_mutex;
- }
- }
- wled->prev_state = !!level;
- unlock_mutex:
- mutex_unlock(&wled->lock);
- }
- /* get api registered with led classdev for wled brightness */
- static enum led_brightness qpnp_wled_get(struct led_classdev *led_cdev)
- {
- struct qpnp_wled *wled;
- wled = container_of(led_cdev, struct qpnp_wled, cdev);
- return wled->cdev.brightness;
- }
- /* set api registered with led classdev for wled brightness */
- static void qpnp_wled_set(struct led_classdev *led_cdev,
- enum led_brightness level)
- {
- struct qpnp_wled *wled;
- wled = container_of(led_cdev, struct qpnp_wled, cdev);
- if (level < LED_OFF)
- level = LED_OFF;
- else if (level > wled->cdev.max_brightness)
- level = wled->cdev.max_brightness;
- wled->cdev.brightness = level;
- queue_work(wled->wq, &wled->work);
- }
- static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
- {
- int rc;
- u8 reg;
- /* display type */
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_DISP_SEL_MASK;
- reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
- rc = qpnp_wled_sec_write_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr),
- reg);
- if (rc)
- return rc;
- if (wled->disp_type_amoled) {
- /* Configure the PSM CTRL register for AMOLED */
- if (wled->vref_psm_mv < QPNP_WLED_VREF_PSM_MIN_MV)
- wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MIN_MV;
- else if (wled->vref_psm_mv > QPNP_WLED_VREF_PSM_MAX_MV)
- wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MAX_MV;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_VREF_PSM_MASK;
- reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
- QPNP_WLED_VREF_PSM_STEP_MV);
- reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
- /* Configure the VLOOP COMP RES register for AMOLED */
- if (wled->loop_comp_res_kohm < QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)
- wled->loop_comp_res_kohm =
- QPNP_WLED_LOOP_COMP_RES_MIN_KOHM;
- else if (wled->loop_comp_res_kohm >
- QPNP_WLED_LOOP_COMP_RES_MAX_KOHM)
- wled->loop_comp_res_kohm =
- QPNP_WLED_LOOP_COMP_RES_MAX_KOHM;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
- ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_VLOOP_COMP_RES_MASK;
- reg |= ((wled->loop_comp_res_kohm -
- QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
- QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
- reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
- reg);
- if (rc)
- return rc;
- /* Configure the CTRL TEST4 register for AMOLED */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_TEST4_REG(wled->ctrl_base), ®);
- if (rc < 0)
- return rc;
- reg |= QPNP_WLED_TEST4_EN_IIND_UP;
- rc = qpnp_wled_sec_write_reg(wled,
- QPNP_WLED_TEST4_REG(base_addr), reg);
- if (rc)
- return rc;
- } else {
- /*
- * enable VREF_UP to avoid false ovp on low brightness for LCD
- */
- reg = QPNP_WLED_TEST4_EN_VREF_UP
- | QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT;
- rc = qpnp_wled_sec_write_reg(wled,
- QPNP_WLED_TEST4_REG(base_addr), reg);
- if (rc)
- return rc;
- }
- return 0;
- }
- #define AUTO_CALIB_BRIGHTNESS 200
- static int wled_auto_calibrate(struct qpnp_wled *wled)
- {
- int rc = 0, i;
- u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
- /* read configured sink configuration */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
- if (rc < 0) {
- pr_err("Failed to read SINK configuration rc=%d\n", rc);
- goto failed_calib;
- }
- /* disable the module before starting calibration */
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK, 0);
- if (rc < 0) {
- pr_err("Failed to disable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- /* set low brightness across all sinks */
- rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS);
- if (rc < 0) {
- pr_err("Failed to set brightness for calibration rc=%d\n", rc);
- goto failed_calib;
- }
- if (wled->en_cabc) {
- for (i = 0; i < wled->max_strings; i++) {
- reg = 0;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_CABC_REG(wled->sink_base, i),
- QPNP_WLED_CABC_MASK, reg);
- if (rc < 0)
- goto failed_calib;
- }
- }
- /* disable all sinks */
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
- if (rc < 0) {
- pr_err("Failed to disable all sinks rc=%d\n", rc);
- goto failed_calib;
- }
- /* iterate through the strings one by one */
- for (i = 0; i < wled->max_strings; i++) {
- sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
- /* Enable feedback control */
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
- i + 1);
- if (rc < 0) {
- pr_err("Failed to enable feedback for SINK %d rc = %d\n",
- i + 1, rc);
- goto failed_calib;
- }
- /* enable the sink */
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test);
- if (rc < 0) {
- pr_err("Failed to configure SINK %d rc=%d\n",
- i + 1, rc);
- goto failed_calib;
- }
- /* Enable the module */
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
- if (rc < 0) {
- pr_err("Failed to enable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- /* delay for WLED soft-start */
- usleep_range(QPNP_WLED_SOFT_START_DLY_US,
- QPNP_WLED_SOFT_START_DLY_US + 1000);
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
- if (rc < 0) {
- pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
- goto failed_calib;
- }
- if (int_sts & QPNP_WLED_OVP_FAULT_BIT)
- pr_debug("WLED OVP fault detected with SINK %d\n",
- i + 1);
- else
- sink_valid |= sink_test;
- /* Disable the module */
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK, 0);
- if (rc < 0) {
- pr_err("Failed to disable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- }
- if (sink_valid == sink_config) {
- pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n",
- sink_config);
- } else {
- pr_warn("Invalid WLED default sink config=%x changing it to=%x\n",
- sink_config, sink_valid);
- sink_config = sink_valid;
- }
- if (!sink_config) {
- pr_warn("No valid WLED sinks found\n");
- wled->module_dis_perm = true;
- goto failed_calib;
- }
- /* write the new sink configuration */
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config);
- if (rc < 0) {
- pr_err("Failed to reconfigure the default sink rc=%d\n", rc);
- goto failed_calib;
- }
- /* MODULATOR_EN setting for valid sinks */
- for (i = 0; i < wled->max_strings; i++) {
- if (wled->en_cabc) {
- reg = 1 << QPNP_WLED_CABC_SHIFT;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_CABC_REG(wled->sink_base, i),
- QPNP_WLED_CABC_MASK, reg);
- if (rc < 0)
- goto failed_calib;
- }
- if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
- reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
- else
- reg = 0x0; /* disable modulator_en for unused sink */
- if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
- reg &= QPNP_WLED_GATE_DRV_MASK;
- else
- reg |= ~QPNP_WLED_GATE_DRV_MASK;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
- if (rc < 0) {
- pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc);
- goto failed_calib;
- }
- }
- /* restore the feedback setting */
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
- wled->fdbk_op);
- if (rc < 0) {
- pr_err("Failed to restore feedback setting rc=%d\n", rc);
- goto failed_calib;
- }
- /* restore brightness */
- rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
- AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
- if (rc < 0) {
- pr_err("Failed to set brightness after calibration rc=%d\n",
- rc);
- goto failed_calib;
- }
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK,
- QPNP_WLED_MODULE_EN_MASK);
- if (rc < 0) {
- pr_err("Failed to enable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- /* delay for WLED soft-start */
- usleep_range(QPNP_WLED_SOFT_START_DLY_US,
- QPNP_WLED_SOFT_START_DLY_US + 1000);
- failed_calib:
- return rc;
- }
- #define WLED_AUTO_CAL_OVP_COUNT 5
- #define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */
- static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
- {
- s64 elapsed_time_us;
- /*
- * Check if the OVP fault was an occasional one
- * or if its firing continuously, the latter qualifies
- * for an auto-calibration check.
- */
- if (!wled->auto_calibration_ovp_count) {
- wled->start_ovp_fault_time = ktime_get();
- wled->auto_calibration_ovp_count++;
- } else {
- elapsed_time_us = ktime_us_delta(ktime_get(),
- wled->start_ovp_fault_time);
- if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US)
- wled->auto_calibration_ovp_count = 0;
- else
- wled->auto_calibration_ovp_count++;
- if (wled->auto_calibration_ovp_count >=
- WLED_AUTO_CAL_OVP_COUNT) {
- wled->auto_calibration_ovp_count = 0;
- return true;
- }
- }
- return false;
- }
- static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
- {
- int rc;
- u8 fault_status = 0, rt_status = 0;
- if (!wled->auto_calib_enabled)
- return 0;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
- if (rc < 0)
- pr_err("Failed to read RT status rc=%d\n", rc);
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
- if (rc < 0)
- pr_err("Failed to read fault status rc=%d\n", rc);
- if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
- (fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
- mutex_lock(&wled->lock);
- rc = wled_auto_calibrate(wled);
- if (rc < 0)
- pr_err("Failed auto-calibration rc=%d\n", rc);
- else
- wled->auto_calib_done = true;
- mutex_unlock(&wled->lock);
- }
- return rc;
- }
- /* ovp irq handler */
- static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
- {
- struct qpnp_wled *wled = _wled;
- int rc;
- u8 fault_sts, int_sts;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
- if (rc < 0) {
- pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
- if (rc < 0) {
- pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
- pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
- int_sts, fault_sts);
- if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
- if (wled->auto_calib_enabled && !wled->auto_calib_done) {
- if (qpnp_wled_auto_cal_required(wled)) {
- mutex_lock(&wled->lock);
- if (wled->ovp_irq > 0 &&
- !wled->ovp_irq_disabled) {
- disable_irq_nosync(wled->ovp_irq);
- wled->ovp_irq_disabled = true;
- }
- rc = wled_auto_calibrate(wled);
- if (rc < 0)
- pr_err("Failed auto-calibration rc=%d\n",
- rc);
- else
- wled->auto_calib_done = true;
- if (wled->ovp_irq > 0 &&
- wled->ovp_irq_disabled) {
- enable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = false;
- }
- mutex_unlock(&wled->lock);
- }
- }
- }
- return IRQ_HANDLED;
- }
- /* short circuit irq handler */
- static irqreturn_t qpnp_wled_sc_irq_handler(int irq, void *_wled)
- {
- struct qpnp_wled *wled = _wled;
- int rc;
- u8 val, ext_ctl;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
- if (rc < 0) {
- pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_EXT_PIN_CTL(wled->ctrl_base), &ext_ctl);
- if (rc < 0) {
- pr_err("Can't read WLED_EXT_PIN_CTL\n");
- return IRQ_HANDLED;
- }
- pr_err("WLED short circuit: n=%d, ext_pin_ctl=%x, fault=%x\n",
- ++wled->sc_cnt, ext_ctl, val);
- mutex_lock(&wled->lock);
- if ((ext_ctl & QPNP_WLED_EXT_PIN_CTL_BIT) == 0x00) {
- /* Disable, then re-enable WLED regulator. */
- qpnp_wled_module_en(wled, wled->ctrl_base, false);
- msleep(QPNP_WLED_SC_DLY_MS);
- qpnp_wled_module_en(wled, wled->ctrl_base, true);
- } else {
- /* Disable, then restore external pin control. */
- qpnp_wled_write_reg(wled,
- QPNP_WLED_EXT_PIN_CTL(wled->ctrl_base),
- ext_ctl & ~QPNP_WLED_EXT_PIN_CTL_BIT);
- msleep(QPNP_WLED_SC_DLY_MS);
- qpnp_wled_write_reg(wled,
- QPNP_WLED_EXT_PIN_CTL(wled->ctrl_base), ext_ctl);
- }
- mutex_unlock(&wled->lock);
- return IRQ_HANDLED;
- }
- static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
- {
- int rc;
- u8 reg = 0;
- /*
- * AVDD trim adjustment is not required for pmi8998/pm660l and not
- * supported for pmi8994.
- */
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
- return false;
- /*
- * Configure TRIM_REG only if disp_type_amoled and it has
- * not already been programmed by bootloader.
- */
- if (!wled->disp_type_amoled)
- return false;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), ®);
- if (rc < 0)
- return false;
- return !(reg & QPNP_WLED_AVDD_SET_BIT);
- }
- static int qpnp_wled_gm_config(struct qpnp_wled *wled)
- {
- int rc;
- u8 mask = 0, reg = 0;
- /* Configure the LOOP COMP GM register */
- if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
- if (wled->disp_type_amoled) {
- reg = 0;
- mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
- } else {
- if (wled->loop_auto_gm_en)
- reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
- if (wled->loop_auto_gm_thresh >
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
- wled->loop_auto_gm_thresh =
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
- reg |= wled->loop_auto_gm_thresh <<
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
- mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
- }
- }
- if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
- wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MIN;
- else if (wled->loop_ea_gm > QPNP_WLED_LOOP_EA_GM_MAX)
- wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MAX;
- reg |= wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
- mask |= QPNP_WLED_VLOOP_COMP_GM_MASK |
- QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base), mask,
- reg);
- if (rc)
- pr_err("write VLOOP_COMP_GM_REG failed, rc=%d]\n", rc);
- return rc;
- }
- static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
- {
- int rc, i, *ovp_table;
- u8 reg;
- /*
- * Configure the OVP register based on ovp_mv only if display type is
- * not AMOLED.
- */
- if (wled->disp_type_amoled)
- return 0;
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- ovp_table = qpnp_wled_ovp_thresholds_pmi8998;
- else
- ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
- for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
- if (wled->ovp_mv == ovp_table[i])
- break;
- }
- if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
- dev_err(&wled->pdev->dev,
- "Invalid ovp threshold specified in device tree\n");
- return -EINVAL;
- }
- reg = i & QPNP_WLED_OVP_MASK;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_OVP_REG(wled->ctrl_base),
- QPNP_WLED_OVP_MASK, reg);
- if (rc)
- return rc;
- return 0;
- }
- static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
- {
- int rc, i;
- u8 reg;
- for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
- if (wled->avdd_target_voltage_mv ==
- qpnp_wled_avdd_target_voltages[i])
- break;
- }
- if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
- dev_err(&wled->pdev->dev,
- "Invalid avdd target voltage specified in device tree\n");
- return -EINVAL;
- }
- /* Update WLED_OVP register based on desired target voltage */
- reg = qpnp_wled_ovp_reg_settings[i];
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_OVP_REG(wled->ctrl_base),
- QPNP_WLED_OVP_MASK, reg);
- if (rc)
- return rc;
- /* Update WLED_TRIM register based on desired target voltage */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), ®);
- if (rc)
- return rc;
- reg += qpnp_wled_avdd_trim_adjustments[i];
- if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
- (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
- dev_dbg(&wled->pdev->dev,
- "adjusted trim %d is not within range, capping it\n",
- (s8)reg);
- if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
- reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
- else
- reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
- }
- reg &= QPNP_WLED_7P7_TRIM_MASK;
- rc = qpnp_wled_sec_write_reg(wled,
- QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), reg);
- if (rc < 0)
- dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
- rc);
- return rc;
- }
- static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
- {
- int rc;
- u8 reg = 0;
- /*
- * At present, configuring the mode to SPMI/SWIRE for controlling
- * AVDD voltage is available only in pmi8998/pm660l.
- */
- if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
- wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
- return 0;
- /* AMOLED_VOUT should be configured for AMOLED */
- if (!wled->disp_type_amoled)
- return 0;
- /* Configure avdd register */
- if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
- dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
- QPNP_WLED_AVDD_MAX_MV);
- wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
- } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
- dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
- QPNP_WLED_AVDD_MIN_MV);
- wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
- }
- if (wled->avdd_mode_spmi) {
- reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
- reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base),
- reg);
- if (rc < 0)
- pr_err("Write to AMOLED_VOUT register failed, rc=%d\n",
- rc);
- } else {
- rc = qpnp_wled_swire_avdd_config(wled);
- if (rc < 0)
- pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
- rc);
- }
- return rc;
- }
- static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
- {
- int rc, i, *ilim_table;
- u8 reg;
- if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
- wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- ilim_table = qpnp_wled_ilim_settings_pmi8998;
- if (wled->ilim_ma > PMI8998_WLED_ILIM_MAX_MA)
- wled->ilim_ma = PMI8998_WLED_ILIM_MAX_MA;
- } else {
- ilim_table = qpnp_wled_ilim_settings_pmi8994;
- if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
- wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
- }
- for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
- if (wled->ilim_ma == ilim_table[i])
- break;
- }
- if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
- dev_err(&wled->pdev->dev,
- "Invalid ilim threshold specified in device tree\n");
- return -EINVAL;
- }
- reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_ILIM_REG(wled->ctrl_base),
- QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, reg);
- if (rc < 0)
- dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
- rc);
- return rc;
- }
- static int qpnp_wled_vref_config(struct qpnp_wled *wled)
- {
- struct wled_vref_setting vref_setting;
- int rc;
- u8 reg = 0;
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- vref_setting = vref_setting_pmi8998;
- else
- vref_setting = vref_setting_pmi8994;
- if (wled->vref_uv < vref_setting.min_uv)
- wled->vref_uv = vref_setting.min_uv;
- else if (wled->vref_uv > vref_setting.max_uv)
- wled->vref_uv = vref_setting.max_uv;
- reg |= DIV_ROUND_CLOSEST(wled->vref_uv - vref_setting.min_uv,
- vref_setting.step_uv);
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_VREF_REG(wled->ctrl_base),
- QPNP_WLED_VREF_MASK, reg);
- if (rc)
- pr_err("Write VREF_REG failed, rc=%d\n", rc);
- return rc;
- }
- /* Configure WLED registers */
- static int qpnp_wled_config(struct qpnp_wled *wled)
- {
- int rc, i, temp;
- u8 reg = 0, sink_en = 0, mask;
- /* Configure display type */
- rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
- if (rc < 0)
- return rc;
- /* Configure the FEEDBACK OUTPUT register */
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
- ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_FDBK_OP_MASK;
- reg |= wled->fdbk_op;
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
- reg);
- if (rc)
- return rc;
- /* Configure the VREF register */
- rc = qpnp_wled_vref_config(wled);
- if (rc < 0) {
- pr_err("Error in configuring wled vref, rc=%d\n", rc);
- return rc;
- }
- /* Configure VLOOP_COMP_GM register */
- rc = qpnp_wled_gm_config(wled);
- if (rc < 0) {
- pr_err("Error in configureing wled gm, rc=%d\n", rc);
- return rc;
- }
- /* Configure the ILIM register */
- rc = qpnp_wled_ilim_config(wled);
- if (rc < 0) {
- pr_err("Error in configuring wled ilim, rc=%d\n", rc);
- return rc;
- }
- /* Configure auto PFM mode for LCD mode only */
- if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- && !wled->disp_type_amoled) {
- reg = 0;
- reg |= wled->lcd_auto_pfm_thresh;
- reg |= wled->lcd_auto_pfm_en <<
- QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_LCD_AUTO_PFM_REG(wled->ctrl_base),
- QPNP_WLED_LCD_AUTO_PFM_EN_BIT |
- QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK, reg);
- if (rc < 0) {
- pr_err("Write LCD_AUTO_PFM failed, rc=%d\n", rc);
- return rc;
- }
- }
- /* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
- reg = (wled->disp_type_amoled) ? 0 : 2;
- mask = SOFTSTART_RAMP_DELAY_MASK;
- if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- && wled->disp_type_amoled) {
- reg |= SOFTSTART_OVERWRITE_BIT;
- mask |= SOFTSTART_OVERWRITE_BIT;
- }
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
- mask, reg);
- if (rc)
- return rc;
- /* Configure the MAX BOOST DUTY register */
- if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
- wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
- else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
- wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base),
- ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_BOOST_DUTY_MASK;
- reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
- /* Configure the SWITCHING FREQ register */
- if (wled->switch_freq_khz == 1600)
- reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
- else
- reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
- /*
- * Do not set the overwrite bit when switching frequency is selected
- * for AMOLED. This register is in logic reset block which can cause
- * the value to be overwritten during module enable/disable.
- */
- mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
- if (!wled->disp_type_amoled)
- reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
- if (rc < 0)
- return rc;
- rc = qpnp_wled_ovp_config(wled);
- if (rc < 0) {
- pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
- return rc;
- }
- if (is_avdd_trim_adjustment_required(wled)) {
- rc = qpnp_wled_avdd_trim_config(wled);
- if (rc < 0)
- return rc;
- }
- rc = qpnp_wled_avdd_mode_config(wled);
- if (rc < 0)
- return rc;
- /* Configure the MODULATION register */
- if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
- temp = 3;
- } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
- temp = 2;
- } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
- temp = 1;
- } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
- temp = 0;
- } else {
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
- temp = 1;
- }
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_MOD_FREQ_MASK;
- reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
- reg &= QPNP_WLED_PHASE_STAG_MASK;
- reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
- reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
- reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);
- reg &= QPNP_WLED_DIM_RES_MASK;
- reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
- if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
- reg &= QPNP_WLED_DIM_HYB_MASK;
- reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
- } else {
- reg &= QPNP_WLED_DIM_HYB_MASK;
- reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
- reg &= QPNP_WLED_DIM_ANA_MASK;
- reg |= wled->dim_mode;
- }
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
- if (rc)
- return rc;
- /* Configure the HYBRID THRESHOLD register */
- if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
- wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
- else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
- wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
- rc = qpnp_wled_read_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
- ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_HYB_THRES_MASK;
- temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
- reg);
- if (rc)
- return rc;
- /* Configure TEST5 register */
- if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) {
- reg = QPNP_WLED_SINK_TEST5_DIG;
- } else {
- reg = QPNP_WLED_SINK_TEST5_HYB;
- if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- reg |= QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT;
- }
- rc = qpnp_wled_sec_write_reg(wled,
- QPNP_WLED_SINK_TEST5_REG(wled->sink_base), reg);
- if (rc)
- return rc;
- /* disable all current sinks and enable selected strings */
- reg = 0x00;
- rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
- reg);
- for (i = 0; i < wled->max_strings; i++) {
- /* SYNC DELAY */
- if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
- wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
- reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
- mask = QPNP_WLED_SYNC_DLY_MASK;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i),
- mask, reg);
- if (rc < 0)
- return rc;
- /* FULL SCALE CURRENT */
- if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
- wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
- reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
- mask = QPNP_WLED_FS_CURR_MASK;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
- mask, reg);
- if (rc < 0)
- return rc;
- /* CABC */
- reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
- mask = QPNP_WLED_CABC_MASK;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_CABC_REG(wled->sink_base, i),
- mask, reg);
- if (rc < 0)
- return rc;
- }
- /* Settings specific to valid sinks */
- for (i = 0; i < wled->num_strings; i++) {
- if (wled->strings[i] >= wled->max_strings) {
- dev_err(&wled->pdev->dev, "Invalid string number\n");
- return -EINVAL;
- }
- /* MODULATOR */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_MOD_EN_REG(wled->sink_base, i), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_MOD_EN_MASK;
- reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
- if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
- reg &= QPNP_WLED_GATE_DRV_MASK;
- else
- reg |= ~QPNP_WLED_GATE_DRV_MASK;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
- if (rc)
- return rc;
- /* SINK EN */
- temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
- sink_en |= (1 << temp);
- }
- mask = QPNP_WLED_CURR_SINK_MASK;
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base),
- mask, sink_en);
- if (rc < 0) {
- dev_err(&wled->pdev->dev,
- "Failed to enable WLED sink config rc = %d\n", rc);
- return rc;
- }
- rc = qpnp_wled_sync_reg_toggle(wled);
- if (rc < 0) {
- dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
- return rc;
- }
- rc = qpnp_wled_auto_calibrate_at_init(wled);
- if (rc < 0)
- pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
- /* setup ovp and sc irqs */
- if (wled->ovp_irq >= 0) {
- irq_set_status_flags(wled->ovp_irq, IRQ_DISABLE_UNLAZY);
- rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
- NULL, qpnp_wled_ovp_irq_handler, IRQF_ONESHOT,
- "qpnp_wled_ovp_irq", wled);
- if (rc < 0) {
- dev_err(&wled->pdev->dev,
- "Unable to request ovp(%d) IRQ(err:%d)\n",
- wled->ovp_irq, rc);
- return rc;
- }
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), ®);
- /* disable the OVP irq only if the module is not enabled */
- if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) {
- disable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = true;
- }
- }
- if (wled->sc_irq >= 0) {
- wled->sc_cnt = 0;
- irq_set_status_flags(wled->sc_irq, IRQ_DISABLE_UNLAZY);
- rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
- NULL, qpnp_wled_sc_irq_handler, IRQF_ONESHOT,
- "qpnp_wled_sc_irq", wled);
- if (rc < 0) {
- dev_err(&wled->pdev->dev,
- "Unable to request sc(%d) IRQ(err:%d)\n",
- wled->sc_irq, rc);
- return rc;
- }
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_SC_PRO_REG(wled->ctrl_base), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_EN_SC_DEB_CYCLES_MASK;
- reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
- if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
- wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
- else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
- wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
- temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
- reg |= (temp << 1);
- if (wled->disp_type_amoled)
- reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
- if (wled->en_ext_pfet_sc_pro) {
- if (!(wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE
- && wled->pmic_rev_id->rev4 ==
- PMI8998_V2P0_REV4)) {
- reg = QPNP_WLED_EXT_FET_DTEST2;
- rc = qpnp_wled_sec_write_reg(wled,
- QPNP_WLED_TEST1_REG(wled->ctrl_base),
- reg);
- if (rc)
- return rc;
- }
- }
- } else {
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_SC_PRO_REG(wled->ctrl_base), ®);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_EN_DEB_CYCLES_MASK;
- if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
- wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
- else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
- wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
- temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
- reg |= (temp << 1);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
- }
- return 0;
- }
- /* parse wled dtsi parameters */
- static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
- {
- struct platform_device *pdev = wled->pdev;
- struct property *prop;
- const char *temp_str;
- u32 temp_val;
- int rc, i, size;
- u8 *strings;
- wled->cdev.name = "wled";
- rc = of_property_read_string(pdev->dev.of_node,
- "linux,name", &wled->cdev.name);
- if (rc && (rc != -EINVAL)) {
- dev_err(&pdev->dev, "Unable to read led name\n");
- return rc;
- }
- wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
- rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
- &wled->cdev.default_trigger);
- if (rc && (rc != -EINVAL)) {
- dev_err(&pdev->dev, "Unable to read led trigger\n");
- return rc;
- }
- if (of_find_property(pdev->dev.of_node, "qcom,wled-brightness-map",
- NULL)) {
- size = of_property_count_elems_of_size(pdev->dev.of_node,
- "qcom,wled-brightness-map", sizeof(u16));
- if (size != NUM_DDIC_CODES) {
- pr_err("Invalid WLED brightness map size:%d\n", size);
- return rc;
- }
- wled->brt_map_table = devm_kcalloc(&pdev->dev, NUM_DDIC_CODES,
- sizeof(u16), GFP_KERNEL);
- if (!wled->brt_map_table)
- return -ENOMEM;
- rc = of_property_read_u16_array(pdev->dev.of_node,
- "qcom,wled-brightness-map", wled->brt_map_table,
- NUM_DDIC_CODES);
- if (rc < 0) {
- pr_err("Error in reading WLED brightness map, rc=%d\n",
- rc);
- return rc;
- }
- for (i = 0; i < NUM_DDIC_CODES; i++) {
- if (wled->brt_map_table[i] > WLED_MAX_LEVEL_4095) {
- pr_err("WLED brightness map not in range\n");
- return -EDOM;
- }
- if ((i > 1) && wled->brt_map_table[i]
- < wled->brt_map_table[i - 1]) {
- pr_err("WLED brightness map not in ascending order?\n");
- return -EDOM;
- }
- }
- }
- wled->stepper_en = of_property_read_bool(pdev->dev.of_node,
- "qcom,wled-stepper-en");
- wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
- "qcom,disp-type-amoled");
- if (wled->disp_type_amoled) {
- wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,vref-psm-mv", &temp_val);
- if (!rc) {
- wled->vref_psm_mv = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vref-psm\n");
- return rc;
- }
- wled->loop_comp_res_kohm = 320;
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- wled->loop_comp_res_kohm = 300;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,loop-comp-res-kohm", &temp_val);
- if (!rc) {
- wled->loop_comp_res_kohm = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
- return rc;
- }
- wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
- "qcom,avdd-mode-spmi");
- wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,avdd-target-voltage-mv", &temp_val);
- if (!rc) {
- wled->avdd_target_voltage_mv = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
- return rc;
- }
- }
- if (wled->disp_type_amoled) {
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- wled->loop_ea_gm =
- QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998;
- else
- wled->loop_ea_gm =
- QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994;
- } else {
- wled->loop_ea_gm = QPNP_WLED_LOOP_GM_DFLT_WLED;
- }
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,loop-ea-gm", &temp_val);
- if (!rc) {
- wled->loop_ea_gm = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
- return rc;
- }
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- wled->loop_auto_gm_en =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,loop-auto-gm-en");
- wled->loop_auto_gm_thresh = QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH;
- rc = of_property_read_u8(pdev->dev.of_node,
- "qcom,loop-auto-gm-thresh",
- &wled->loop_auto_gm_thresh);
- if (rc && rc != -EINVAL) {
- dev_err(&pdev->dev,
- "Unable to read loop-auto-gm-thresh\n");
- return rc;
- }
- }
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
- wled->pmic_rev_id->rev4 == PMI8998_V2P0_REV4)
- wled->lcd_auto_pfm_en = false;
- else
- wled->lcd_auto_pfm_en = true;
- wled->lcd_auto_pfm_thresh = QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH;
- rc = of_property_read_u8(pdev->dev.of_node,
- "qcom,lcd-auto-pfm-thresh",
- &wled->lcd_auto_pfm_thresh);
- if (rc && rc != -EINVAL) {
- dev_err(&pdev->dev,
- "Unable to read lcd-auto-pfm-thresh\n");
- return rc;
- }
- if (wled->lcd_auto_pfm_thresh >
- QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX)
- wled->lcd_auto_pfm_thresh =
- QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX;
- }
- wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,sc-deb-cycles", &temp_val);
- if (!rc) {
- wled->sc_deb_cycles = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
- return rc;
- }
- wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
- rc = of_property_read_string(pdev->dev.of_node,
- "qcom,fdbk-output", &temp_str);
- if (!rc) {
- if (strcmp(temp_str, "wled1") == 0)
- wled->fdbk_op = QPNP_WLED_FDBK_WLED1;
- else if (strcmp(temp_str, "wled2") == 0)
- wled->fdbk_op = QPNP_WLED_FDBK_WLED2;
- else if (strcmp(temp_str, "wled3") == 0)
- wled->fdbk_op = QPNP_WLED_FDBK_WLED3;
- else if (strcmp(temp_str, "wled4") == 0)
- wled->fdbk_op = QPNP_WLED_FDBK_WLED4;
- else
- wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read feedback output\n");
- return rc;
- }
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- wled->vref_uv = vref_setting_pmi8998.default_uv;
- else
- wled->vref_uv = vref_setting_pmi8994.default_uv;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,vref-uv", &temp_val);
- if (!rc) {
- wled->vref_uv = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vref\n");
- return rc;
- }
- wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,switch-freq-khz", &temp_val);
- if (!rc) {
- wled->switch_freq_khz = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read switch freq\n");
- return rc;
- }
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- wled->ovp_mv = 29600;
- else
- wled->ovp_mv = 29500;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,ovp-mv", &temp_val);
- if (!rc) {
- wled->ovp_mv = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ovp\n");
- return rc;
- }
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- if (wled->disp_type_amoled)
- wled->ilim_ma = PMI8998_AMOLED_DFLT_ILIM_MA;
- else
- wled->ilim_ma = PMI8998_WLED_DFLT_ILIM_MA;
- } else {
- if (wled->disp_type_amoled)
- wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
- else
- wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
- }
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,ilim-ma", &temp_val);
- if (!rc) {
- wled->ilim_ma = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ilim\n");
- return rc;
- }
- wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,boost-duty-ns", &temp_val);
- if (!rc) {
- wled->boost_duty_ns = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read boost duty\n");
- return rc;
- }
- wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,mod-freq-khz", &temp_val);
- if (!rc) {
- wled->mod_freq_khz = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read modulation freq\n");
- return rc;
- }
- wled->dim_mode = QPNP_WLED_DIM_HYBRID;
- rc = of_property_read_string(pdev->dev.of_node,
- "qcom,dim-mode", &temp_str);
- if (!rc) {
- if (strcmp(temp_str, "analog") == 0)
- wled->dim_mode = QPNP_WLED_DIM_ANALOG;
- else if (strcmp(temp_str, "digital") == 0)
- wled->dim_mode = QPNP_WLED_DIM_DIGITAL;
- else
- wled->dim_mode = QPNP_WLED_DIM_HYBRID;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read dim mode\n");
- return rc;
- }
- if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
- wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,hyb-thres", &temp_val);
- if (!rc) {
- wled->hyb_thres = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read hyb threshold\n");
- return rc;
- }
- }
- wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,sync-dly-us", &temp_val);
- if (!rc) {
- wled->sync_dly_us = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read sync delay\n");
- return rc;
- }
- wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,fs-curr-ua", &temp_val);
- if (!rc) {
- wled->fs_curr_ua = temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read full scale current\n");
- return rc;
- }
- wled->cons_sync_write_delay_us = 0;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,cons-sync-write-delay-us", &temp_val);
- if (!rc)
- wled->cons_sync_write_delay_us = temp_val;
- wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
- "qcom,en-9b-dim-res");
- wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
- "qcom,en-phase-stag");
- wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
- "qcom,en-cabc");
- if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
- wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS;
- else
- wled->max_strings = QPNP_WLED_MAX_STRINGS;
- prop = of_find_property(pdev->dev.of_node,
- "qcom,led-strings-list", &temp_val);
- if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
- dev_err(&pdev->dev, "Invalid strings info, use default");
- wled->num_strings = wled->max_strings;
- for (i = 0; i < wled->num_strings; i++)
- wled->strings[i] = i;
- } else {
- wled->num_strings = temp_val;
- strings = prop->value;
- for (i = 0; i < wled->num_strings; ++i)
- wled->strings[i] = strings[i];
- }
- wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
- if (wled->ovp_irq < 0)
- dev_dbg(&pdev->dev, "ovp irq is not used\n");
- wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
- if (wled->sc_irq < 0)
- dev_dbg(&pdev->dev, "sc irq is not used\n");
- wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
- "qcom,en-ext-pfet-sc-pro");
- wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
- "qcom,lcd-psm-ctrl");
- wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
- "qcom,auto-calibration-enable");
- return 0;
- }
- static int qpnp_wled_probe(struct platform_device *pdev)
- {
- struct qpnp_wled *wled;
- struct device_node *revid_node;
- int rc = 0, i;
- const __be32 *prop;
- wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
- if (!wled)
- return -ENOMEM;
- wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!wled->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
- wled->pdev = pdev;
- revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
- if (!revid_node) {
- pr_err("Missing qcom,pmic-revid property - driver failed\n");
- return -EINVAL;
- }
- wled->pmic_rev_id = get_revid_data(revid_node);
- of_node_put(revid_node);
- if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
- pr_err("Unable to get pmic_revid rc=%ld\n",
- PTR_ERR(wled->pmic_rev_id));
- /*
- * the revid peripheral must be registered, any failure
- * here only indicates that the rev-id module has not
- * probed yet.
- */
- return -EPROBE_DEFER;
- }
- pr_debug("PMIC subtype %d Digital major %d\n",
- wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
- wled->wq = alloc_ordered_workqueue("qpnp_wled_wq", WQ_HIGHPRI);
- if (!wled->wq) {
- pr_err("Unable to alloc workqueue for WLED\n");
- return -ENOMEM;
- }
- prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
- NULL, NULL);
- if (!prop) {
- dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
- return rc;
- }
- wled->sink_base = be32_to_cpu(*prop);
- prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
- NULL, NULL);
- if (!prop) {
- dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
- return rc;
- }
- wled->ctrl_base = be32_to_cpu(*prop);
- dev_set_drvdata(&pdev->dev, wled);
- rc = qpnp_wled_parse_dt(wled);
- if (rc) {
- dev_err(&pdev->dev, "DT parsing failed\n");
- return rc;
- }
- mutex_init(&wled->bus_lock);
- mutex_init(&wled->lock);
- rc = qpnp_wled_config(wled);
- if (rc) {
- dev_err(&pdev->dev, "wled config failed\n");
- return rc;
- }
- INIT_WORK(&wled->work, qpnp_wled_work);
- wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
- wled->ramp_step = 1;
- wled->cdev.brightness_set = qpnp_wled_set;
- wled->cdev.brightness_get = qpnp_wled_get;
- wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
- rc = led_classdev_register(&pdev->dev, &wled->cdev);
- if (rc) {
- dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
- goto wled_register_fail;
- }
- for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++) {
- rc = sysfs_create_file(&wled->cdev.dev->kobj,
- &qpnp_wled_attrs[i].attr);
- if (rc < 0) {
- dev_err(&pdev->dev, "sysfs creation failed\n");
- goto sysfs_fail;
- }
- }
- return 0;
- sysfs_fail:
- for (i--; i >= 0; i--)
- sysfs_remove_file(&wled->cdev.dev->kobj,
- &qpnp_wled_attrs[i].attr);
- led_classdev_unregister(&wled->cdev);
- wled_register_fail:
- cancel_work_sync(&wled->work);
- destroy_workqueue(wled->wq);
- mutex_destroy(&wled->lock);
- return rc;
- }
- static int qpnp_wled_remove(struct platform_device *pdev)
- {
- struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
- int i;
- for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
- sysfs_remove_file(&wled->cdev.dev->kobj,
- &qpnp_wled_attrs[i].attr);
- led_classdev_unregister(&wled->cdev);
- cancel_work_sync(&wled->work);
- destroy_workqueue(wled->wq);
- mutex_destroy(&wled->lock);
- return 0;
- }
- static const struct of_device_id spmi_match_table[] = {
- { .compatible = "qcom,qpnp-wled",},
- { },
- };
- static struct platform_driver qpnp_wled_driver = {
- .driver = {
- .name = "qcom,qpnp-wled",
- .of_match_table = spmi_match_table,
- },
- .probe = qpnp_wled_probe,
- .remove = qpnp_wled_remove,
- };
- static int __init qpnp_wled_init(void)
- {
- return platform_driver_register(&qpnp_wled_driver);
- }
- subsys_initcall(qpnp_wled_init);
- static void __exit qpnp_wled_exit(void)
- {
- platform_driver_unregister(&qpnp_wled_driver);
- }
- module_exit(qpnp_wled_exit);
- MODULE_DESCRIPTION("QPNP WLED driver");
- MODULE_LICENSE("GPL v2");
- MODULE_ALIAS("leds:leds-qpnp-wled");
|