Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Shenghao Ding | 9937 | 99.62% | 1 | 20.00% |
Rob Herring | 34 | 0.34% | 1 | 20.00% |
gehao | 2 | 0.02% | 1 | 20.00% |
Al Viro | 1 | 0.01% | 1 | 20.00% |
Colin Ian King | 1 | 0.01% | 1 | 20.00% |
Total | 9975 | 5 |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186
// SPDX-License-Identifier: GPL-2.0 // // ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC Device // // Copyright (C) 2022 - 2024 Texas Instruments Incorporated // https://www.ti.com // // The PCM6240 driver implements a flexible and configurable // algo coefficient setting for one, two, or even multiple // PCM6240 Family chips. // // Author: Shenghao Ding <shenghao-ding@ti.com> // #include <linux/unaligned.h> #include <linux/firmware.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/regmap.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> #include "pcm6240.h" static const struct i2c_device_id pcmdevice_i2c_id[] = { { "adc3120", ADC3120 }, { "adc5120", ADC5120 }, { "adc6120", ADC6120 }, { "dix4192", DIX4192 }, { "pcm1690", PCM1690 }, { "pcm3120", PCM3120 }, { "pcm3140", PCM3140 }, { "pcm5120", PCM5120 }, { "pcm5140", PCM5140 }, { "pcm6120", PCM6120 }, { "pcm6140", PCM6140 }, { "pcm6240", PCM6240 }, { "pcm6260", PCM6260 }, { "pcm9211", PCM9211 }, { "pcmd3140", PCMD3140 }, { "pcmd3180", PCMD3180 }, { "pcmd512x", PCMD512X }, { "taa5212", TAA5212 }, { "taa5412", TAA5412 }, { "tad5212", TAD5212 }, { "tad5412", TAD5412 }, {} }; MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id); static const char *const pcmdev_ctrl_name[] = { "%s i2c%d Dev%d Ch%d Ana Volume", "%s i2c%d Dev%d Ch%d Digi Volume", "%s i2c%d Dev%d Ch%d Fine Volume", }; static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = { { .shift = 1, .reg = ADC5120_REG_CH1_ANALOG_GAIN, .max = 0x54, .invert = 0, }, { .shift = 1, .reg = ADC5120_REG_CH2_ANALOG_GAIN, .max = 0x54, .invert = 0, } }; static const struct pcmdevice_mixer_control adc5120_digi_gain_ctl[] = { { .shift = 0, .reg = ADC5120_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = ADC5120_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm1690_digi_gain_ctl[] = { { .shift = 0, .reg = PCM1690_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH3_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH4_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH5_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH6_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH7_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM1690_REG_CH8_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm6240_analog_gain_ctl[] = { { .shift = 2, .reg = PCM6240_REG_CH1_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6240_REG_CH2_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6240_REG_CH3_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6240_REG_CH4_ANALOG_GAIN, .max = 0x42, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm6240_digi_gain_ctl[] = { { .shift = 0, .reg = PCM6240_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6240_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6240_REG_CH3_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6240_REG_CH4_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm6260_analog_gain_ctl[] = { { .shift = 2, .reg = PCM6260_REG_CH1_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6260_REG_CH2_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6260_REG_CH3_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6260_REG_CH4_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6260_REG_CH5_ANALOG_GAIN, .max = 0x42, .invert = 0, }, { .shift = 2, .reg = PCM6260_REG_CH6_ANALOG_GAIN, .max = 0x42, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm6260_digi_gain_ctl[] = { { .shift = 0, .reg = PCM6260_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6260_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6260_REG_CH3_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6260_REG_CH4_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6260_REG_CH5_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM6260_REG_CH6_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcm9211_digi_gain_ctl[] = { { .shift = 0, .reg = PCM9211_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCM9211_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcmd3140_digi_gain_ctl[] = { { .shift = 0, .reg = PCMD3140_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3140_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3140_REG_CH3_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3140_REG_CH4_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcmd3140_fine_gain_ctl[] = { { .shift = 4, .reg = PCMD3140_REG_CH1_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3140_REG_CH2_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3140_REG_CH3_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3140_REG_CH4_FINE_GAIN, .max = 0xf, .invert = 0, } }; static const struct pcmdevice_mixer_control pcmd3180_digi_gain_ctl[] = { { .shift = 0, .reg = PCMD3180_REG_CH1_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH2_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH3_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH4_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH5_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH6_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH7_DIGITAL_GAIN, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = PCMD3180_REG_CH8_DIGITAL_GAIN, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control pcmd3180_fine_gain_ctl[] = { { .shift = 4, .reg = PCMD3180_REG_CH1_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH2_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH3_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH4_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH5_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH6_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH7_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = PCMD3180_REG_CH8_FINE_GAIN, .max = 0xf, .invert = 0, } }; static const struct pcmdevice_mixer_control taa5412_digi_vol_ctl[] = { { .shift = 0, .reg = TAA5412_REG_CH1_DIGITAL_VOLUME, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = TAA5412_REG_CH2_DIGITAL_VOLUME, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = TAA5412_REG_CH3_DIGITAL_VOLUME, .max = 0xff, .invert = 0, }, { .shift = 0, .reg = TAA5412_REG_CH4_DIGITAL_VOLUME, .max = 0xff, .invert = 0, } }; static const struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = { { .shift = 4, .reg = TAA5412_REG_CH1_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = TAA5412_REG_CH2_FINE_GAIN, .max = 0xf, .invert = 0, }, { .shift = 4, .reg = TAA5412_REG_CH3_FINE_GAIN, .max = 0xf, .invert = 4, }, { .shift = 0, .reg = TAA5412_REG_CH4_FINE_GAIN, .max = 0xf, .invert = 4, } }; static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv, -10000, 2700); static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv, -12750, 0); static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_dig_gain_tlv, -25500, 0); static const DECLARE_TLV_DB_MINMAX_MUTE(pcm9211_dig_gain_tlv, -11450, 2000); static const DECLARE_TLV_DB_MINMAX_MUTE(adc5120_fgain_tlv, -10050, 2700); static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200); static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv, -10000, 2700); static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200); static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv, -8050, 4700); static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv, -80, 70); static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv, unsigned short dev_no) { struct i2c_client *client = (struct i2c_client *)pcm_priv->client; struct regmap *map = pcm_priv->regmap; int ret; if (client->addr == pcm_priv->addr[dev_no]) return 0; client->addr = pcm_priv->addr[dev_no]; /* All pcmdevices share the same regmap, clear the page * inside regmap once switching to another pcmdevice. * Register 0 at any pages inside pcmdevice is the same * one for page-switching. */ ret = regmap_write(map, PCMDEVICE_PAGE_SELECT, 0); if (ret < 0) dev_err(pcm_priv->dev, "%s: err = %d\n", __func__, ret); return ret; } static int pcmdev_dev_read(struct pcmdevice_priv *pcm_dev, unsigned int dev_no, unsigned int reg, unsigned int *val) { struct regmap *map = pcm_dev->regmap; int ret; if (dev_no >= pcm_dev->ndev) { dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__, dev_no); return -EINVAL; } ret = pcmdev_change_dev(pcm_dev, dev_no); if (ret < 0) { dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret); return ret; } ret = regmap_read(map, reg, val); if (ret < 0) dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret); return ret; } static int pcmdev_dev_update_bits(struct pcmdevice_priv *pcm_dev, unsigned int dev_no, unsigned int reg, unsigned int mask, unsigned int value) { struct regmap *map = pcm_dev->regmap; int ret; if (dev_no >= pcm_dev->ndev) { dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__, dev_no); return -EINVAL; } ret = pcmdev_change_dev(pcm_dev, dev_no); if (ret < 0) { dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret); return ret; } ret = regmap_update_bits(map, reg, mask, value); if (ret < 0) dev_err(pcm_dev->dev, "%s: update_bits err=%d\n", __func__, ret); return ret; } static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(component); struct pcmdevice_mixer_control *mc = (struct pcmdevice_mixer_control *)kcontrol->private_value; int max = mc->max, ret; unsigned int mask = BIT(fls(max)) - 1; unsigned int dev_no = mc->dev_no; unsigned int shift = mc->shift; unsigned int reg = mc->reg; unsigned int val; mutex_lock(&pcm_dev->codec_lock); if (pcm_dev->chip_id == PCM1690) { ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, &val); if (ret) { dev_err(pcm_dev->dev, "%s: read mode err=%d\n", __func__, ret); goto out; } val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; /* Set to wide-range mode, before using vol ctrl. */ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { ucontrol->value.integer.value[0] = -25500; goto out; } /* Set to fine mode, before using fine vol ctrl. */ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { ucontrol->value.integer.value[0] = -12750; goto out; } } ret = pcmdev_dev_read(pcm_dev, dev_no, reg, &val); if (ret) { dev_err(pcm_dev->dev, "%s: read err=%d\n", __func__, ret); goto out; } val = (val >> shift) & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; ucontrol->value.integer.value[0] = val; out: mutex_unlock(&pcm_dev->codec_lock); return ret; } static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL); } static int pcm1690_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL); } static int pcm1690_get_finevolsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_FINE_VOL_CTRL); } static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(component); struct pcmdevice_mixer_control *mc = (struct pcmdevice_mixer_control *)kcontrol->private_value; int max = mc->max, rc; unsigned int mask = BIT(fls(max)) - 1; unsigned int dev_no = mc->dev_no; unsigned int shift = mc->shift; unsigned int val, val_mask; unsigned int reg = mc->reg; mutex_lock(&pcm_dev->codec_lock); val = ucontrol->value.integer.value[0] & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; val_mask = mask << shift; val = val << shift; switch (vol_ctrl_type) { case PCMDEV_PCM1690_VOL_CTRL: val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK; val |= PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE; break; case PCMDEV_PCM1690_FINE_VOL_CTRL: val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK; val |= PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP; break; } rc = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, val_mask, val); if (rc < 0) dev_err(pcm_dev->dev, "%s: update_bits err = %d\n", __func__, rc); else rc = 1; mutex_unlock(&pcm_dev->codec_lock); return rc; } static int pcmdevice_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL); } static int pcm1690_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL); } static int pcm1690_put_finevolsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_FINE_VOL_CTRL); } static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = { // ADC3120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // ADC5120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // ADC6120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // DIX4192 { { .ctrl_array_size = 0, }, { .ctrl_array_size = 0, }, }, // PCM1690 { { .gain = pcm1690_fine_dig_gain_tlv, .pcmdev_ctrl = pcm1690_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl), .get = pcm1690_get_volsw, .put = pcm1690_put_volsw, .pcmdev_ctrl_name_id = 1, }, { .gain = pcm1690_dig_gain_tlv, .pcmdev_ctrl = pcm1690_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl), .get = pcm1690_get_finevolsw, .put = pcm1690_put_finevolsw, .pcmdev_ctrl_name_id = 2, }, }, // PCM3120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM3140 { { .gain = pcm6260_chgain_tlv, .pcmdev_ctrl = pcm6240_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = pcm6260_fgain_tlv, .pcmdev_ctrl = pcm6240_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM5120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM5140 { { .gain = pcm6260_chgain_tlv, .pcmdev_ctrl = pcm6240_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = pcm6260_fgain_tlv, .pcmdev_ctrl = pcm6240_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM6120 { { .gain = adc5120_chgain_tlv, .pcmdev_ctrl = adc5120_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = adc5120_fgain_tlv, .pcmdev_ctrl = adc5120_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM6140 { { .gain = pcm6260_chgain_tlv, .pcmdev_ctrl = pcm6240_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = pcm6260_fgain_tlv, .pcmdev_ctrl = pcm6240_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM6240 { { .gain = pcm6260_chgain_tlv, .pcmdev_ctrl = pcm6240_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = pcm6260_fgain_tlv, .pcmdev_ctrl = pcm6240_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM6260 { { .gain = pcm6260_chgain_tlv, .pcmdev_ctrl = pcm6260_analog_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6260_analog_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 0, }, { .gain = pcm6260_fgain_tlv, .pcmdev_ctrl = pcm6260_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm6260_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCM9211 { { .ctrl_array_size = 0, }, { .gain = pcm9211_dig_gain_tlv, .pcmdev_ctrl = pcm9211_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcm9211_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCMD3140 { { .gain = taa5412_fine_gain_tlv, .pcmdev_ctrl = pcmd3140_fine_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcmd3140_fine_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 2, }, { .gain = pcmd3140_dig_gain_tlv, .pcmdev_ctrl = pcmd3140_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcmd3140_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCMD3180 { { .gain = taa5412_fine_gain_tlv, .pcmdev_ctrl = pcmd3180_fine_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcmd3180_fine_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 2, }, { .gain = pcmd3140_dig_gain_tlv, .pcmdev_ctrl = pcmd3180_digi_gain_ctl, .ctrl_array_size = ARRAY_SIZE(pcmd3180_digi_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // PCMD512X { { .ctrl_array_size = 0, }, { .ctrl_array_size = 0, }, }, // TAA5212 { { .gain = taa5412_fine_gain_tlv, .pcmdev_ctrl = taa5412_fine_gain_ctl, .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 2, }, { .gain = taa5412_dig_vol_tlv, .pcmdev_ctrl = taa5412_digi_vol_ctl, .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // TAA5412 { { .gain = taa5412_fine_gain_tlv, .pcmdev_ctrl = taa5412_fine_gain_ctl, .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 2, }, { .gain = taa5412_dig_vol_tlv, .pcmdev_ctrl = taa5412_digi_vol_ctl, .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl), .get = pcmdevice_get_volsw, .put = pcmdevice_put_volsw, .pcmdev_ctrl_name_id = 1, }, }, // TAD5212 { { .ctrl_array_size = 0, }, { .ctrl_array_size = 0, }, }, // TAD5412 { { .ctrl_array_size = 0, }, { .ctrl_array_size = 0, }, }, }; static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev, unsigned int dev_no, unsigned int reg, unsigned char *data, unsigned int len) { struct regmap *map = pcm_dev->regmap; int ret; if (dev_no >= pcm_dev->ndev) { dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__, dev_no); return -EINVAL; } ret = pcmdev_change_dev(pcm_dev, dev_no); if (ret < 0) { dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret); return ret; } ret = regmap_bulk_write(map, reg, data, len); if (ret < 0) dev_err(pcm_dev->dev, "%s: bulk_write err = %d\n", __func__, ret); return ret; } static int pcmdev_dev_write(struct pcmdevice_priv *pcm_dev, unsigned int dev_no, unsigned int reg, unsigned int value) { struct regmap *map = pcm_dev->regmap; int ret; if (dev_no >= pcm_dev->ndev) { dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__, dev_no); return -EINVAL; } ret = pcmdev_change_dev(pcm_dev, dev_no); if (ret < 0) { dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret); return ret; } ret = regmap_write(map, reg, value); if (ret < 0) dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret); return ret; } static int pcmdevice_info_profile( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = max(0, pcm_dev->regbin.ncfgs - 1); return 0; } static int pcmdevice_get_profile_id( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); ucontrol->value.integer.value[0] = pcm_dev->cur_conf; return 0; } static int pcmdevice_set_profile_id( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); int nr_profile = ucontrol->value.integer.value[0]; int max = pcm_dev->regbin.ncfgs - 1; int ret = 0; nr_profile = clamp(nr_profile, 0, max); if (pcm_dev->cur_conf != nr_profile) { pcm_dev->cur_conf = nr_profile; ret = 1; } return ret; } static int pcmdevice_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct pcmdevice_mixer_control *mc = (struct pcmdevice_mixer_control *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = mc->max; return 0; } static void pcm9211_sw_rst(struct pcmdevice_priv *pcm_dev) { int ret, i; for (i = 0; i < pcm_dev->ndev; i++) { ret = pcmdev_dev_update_bits(pcm_dev, i, PCM9211_REG_SW_CTRL, PCM9211_REG_SW_CTRL_MRST_MSK, PCM9211_REG_SW_CTRL_MRST); if (ret < 0) dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n", __func__, i, ret); } } static void pcmdevice_sw_rst(struct pcmdevice_priv *pcm_dev) { int ret, i; for (i = 0; i < pcm_dev->ndev; i++) { ret = pcmdev_dev_write(pcm_dev, i, PCMDEVICE_REG_SWRESET, PCMDEVICE_REG_SWRESET_RESET); if (ret < 0) dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n", __func__, i, ret); } } static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt, const unsigned char *config_data, unsigned int config_size, int *status) { struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; struct pcmdevice_config_info *cfg_info; struct pcmdevice_block_data **bk_da; unsigned int config_offset = 0, i; cfg_info = kzalloc(sizeof(struct pcmdevice_config_info), GFP_KERNEL); if (!cfg_info) { *status = -ENOMEM; goto out; } if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) { if (config_offset + 64 > (int)config_size) { *status = -EINVAL; dev_err(pcm_dev->dev, "%s: cfg_name out of boundary\n", __func__); goto out; } memcpy(cfg_info->cfg_name, &config_data[config_offset], 64); config_offset += 64; } if (config_offset + 4 > config_size) { *status = -EINVAL; dev_err(pcm_dev->dev, "%s: nblocks out of boundary\n", __func__); goto out; } cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, sizeof(struct pcmdevice_block_data *), GFP_KERNEL); if (!bk_da) { *status = -ENOMEM; goto out; } cfg_info->real_nblocks = 0; for (i = 0; i < cfg_info->nblocks; i++) { if (config_offset + 12 > config_size) { *status = -EINVAL; dev_err(pcm_dev->dev, "%s: out of boundary i = %d nblocks = %u\n", __func__, i, cfg_info->nblocks); break; } bk_da[i] = kzalloc(sizeof(struct pcmdevice_block_data), GFP_KERNEL); if (!bk_da[i]) { *status = -ENOMEM; break; } bk_da[i]->dev_idx = config_data[config_offset]; config_offset++; bk_da[i]->block_type = config_data[config_offset]; config_offset++; if (bk_da[i]->block_type == PCMDEVICE_BIN_BLK_PRE_POWER_UP) { if (bk_da[i]->dev_idx == 0) cfg_info->active_dev = (1 << pcm_dev->ndev) - 1; else cfg_info->active_dev = 1 << (bk_da[i]->dev_idx - 1); } bk_da[i]->yram_checksum = get_unaligned_be16(&config_data[config_offset]); config_offset += 2; bk_da[i]->block_size = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; bk_da[i]->n_subblks = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; if (config_offset + bk_da[i]->block_size > config_size) { *status = -EINVAL; dev_err(pcm_dev->dev, "%s: out of boundary: i = %d blks = %u\n", __func__, i, cfg_info->nblocks); break; } bk_da[i]->regdata = kmemdup(&config_data[config_offset], bk_da[i]->block_size, GFP_KERNEL); if (!bk_da[i]->regdata) { *status = -ENOMEM; goto out; } config_offset += bk_da[i]->block_size; cfg_info->real_nblocks += 1; } out: return cfg_info; } static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev, int dev_no, int ctl_id) { struct i2c_adapter *adap = pcm_dev->client->adapter; struct snd_soc_component *comp = pcm_dev->component; struct pcmdevice_mixer_control *pcmdev_ctrl; struct snd_kcontrol_new *pcmdev_controls; int ret, mix_index = 0, name_id, chn; unsigned int id = pcm_dev->chip_id; const int nr_chn = pcmdev_gain_ctl_info[id][ctl_id].ctrl_array_size; const char *ctrl_name; char *name; if (!nr_chn) { dev_dbg(pcm_dev->dev, "%s: no gain ctrl for %s\n", __func__, pcm_dev->dev_name); return 0; } pcmdev_controls = devm_kzalloc(pcm_dev->dev, nr_chn * sizeof(struct snd_kcontrol_new), GFP_KERNEL); if (!pcmdev_controls) return -ENOMEM; name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id; ctrl_name = pcmdev_ctrl_name[name_id]; for (chn = 1; chn <= nr_chn; chn++) { name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); if (!name) { ret = -ENOMEM; goto out; } scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, ctrl_name, pcm_dev->upper_dev_name, adap->nr, dev_no, chn); pcmdev_controls[mix_index].tlv.p = pcmdev_gain_ctl_info[id][ctl_id].gain; pcmdev_ctrl = devm_kmemdup(pcm_dev->dev, &pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl[chn - 1], sizeof(*pcmdev_ctrl), GFP_KERNEL); if (!pcmdev_ctrl) { ret = -ENOMEM; goto out; } pcmdev_ctrl->dev_no = dev_no; pcmdev_controls[mix_index].private_value = (unsigned long)pcmdev_ctrl; pcmdev_controls[mix_index].name = name; pcmdev_controls[mix_index].access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE; pcmdev_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; pcmdev_controls[mix_index].info = pcmdevice_info_volsw; pcmdev_controls[mix_index].get = pcmdev_gain_ctl_info[id][ctl_id].get; pcmdev_controls[mix_index].put = pcmdev_gain_ctl_info[id][ctl_id].put; mix_index++; } ret = snd_soc_add_component_controls(comp, pcmdev_controls, mix_index); if (ret) dev_err(pcm_dev->dev, "%s: add_controls err = %d\n", __func__, ret); out: return ret; } static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev) { struct snd_soc_component *comp = pcm_dev->component; struct i2c_adapter *adap = pcm_dev->client->adapter; struct snd_kcontrol_new *pcmdev_ctrl; char *name; int ret; pcmdev_ctrl = devm_kzalloc(pcm_dev->dev, sizeof(struct snd_kcontrol_new), GFP_KERNEL); if (!pcmdev_ctrl) return -ENOMEM; /* Create a mixer item for selecting the active profile */ name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); if (!name) return -ENOMEM; scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr); pcmdev_ctrl->name = name; pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; pcmdev_ctrl->info = pcmdevice_info_profile; pcmdev_ctrl->get = pcmdevice_get_profile_id; pcmdev_ctrl->put = pcmdevice_set_profile_id; ret = snd_soc_add_component_controls(comp, pcmdev_ctrl, 1); if (ret) dev_err(pcm_dev->dev, "%s: add_controls err = %d\n", __func__, ret); return ret; } static void pcmdevice_config_info_remove(void *ctxt) { struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *) ctxt; struct pcmdevice_regbin *regbin = &(pcm_dev->regbin); struct pcmdevice_config_info **cfg_info = regbin->cfg_info; int i, j; if (!cfg_info) return; for (i = 0; i < regbin->ncfgs; i++) { if (!cfg_info[i]) continue; if (cfg_info[i]->blk_data) { for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) { if (!cfg_info[i]->blk_data[j]) continue; kfree(cfg_info[i]->blk_data[j]->regdata); kfree(cfg_info[i]->blk_data[j]); } kfree(cfg_info[i]->blk_data); } kfree(cfg_info[i]); } kfree(cfg_info); } static int pcmdev_regbin_ready(const struct firmware *fmw, void *ctxt) { struct pcmdevice_config_info **cfg_info; struct pcmdevice_priv *pcm_dev = ctxt; struct pcmdevice_regbin_hdr *fw_hdr; struct pcmdevice_regbin *regbin; unsigned int total_config_sz = 0; int offset = 0, ret = 0, i; unsigned char *buf; regbin = &(pcm_dev->regbin); fw_hdr = &(regbin->fw_hdr); if (!fmw || !fmw->data) { dev_err(pcm_dev->dev, "%s: failed to read %s\n", __func__, pcm_dev->bin_name); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } buf = (unsigned char *)fmw->data; fw_hdr->img_sz = get_unaligned_be32(&buf[offset]); offset += 4; if (fw_hdr->img_sz != fmw->size) { dev_err(pcm_dev->dev, "%s: file size(%d) not match %u", __func__, (int)fmw->size, fw_hdr->img_sz); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } fw_hdr->checksum = get_unaligned_be32(&buf[offset]); offset += 4; fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]); if (fw_hdr->binary_version_num < 0x103) { dev_err(pcm_dev->dev, "%s: bin version 0x%04x is out of date", __func__, fw_hdr->binary_version_num); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } offset += 4; fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]); offset += 8; fw_hdr->plat_type = buf[offset]; offset += 1; fw_hdr->dev_family = buf[offset]; offset += 1; fw_hdr->reserve = buf[offset]; offset += 1; fw_hdr->ndev = buf[offset]; offset += 1; if (fw_hdr->ndev != pcm_dev->ndev) { dev_err(pcm_dev->dev, "%s: invalid ndev(%u)\n", __func__, fw_hdr->ndev); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } if (offset + PCMDEVICE_MAX_REGBIN_DEVICES > fw_hdr->img_sz) { dev_err(pcm_dev->dev, "%s: devs out of boundary!\n", __func__); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } for (i = 0; i < PCMDEVICE_MAX_REGBIN_DEVICES; i++, offset++) fw_hdr->devs[i] = buf[offset]; fw_hdr->nconfig = get_unaligned_be32(&buf[offset]); offset += 4; for (i = 0; i < PCMDEVICE_CONFIG_SUM; i++) { fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]); offset += 4; total_config_sz += fw_hdr->config_size[i]; } if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { dev_err(pcm_dev->dev, "%s: bin file error!\n", __func__); pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -EINVAL; goto out; } cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); if (!cfg_info) { pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; ret = -ENOMEM; goto out; } regbin->cfg_info = cfg_info; regbin->ncfgs = 0; for (i = 0; i < (int)fw_hdr->nconfig; i++) { cfg_info[i] = pcmdevice_add_config(ctxt, &buf[offset], fw_hdr->config_size[i], &ret); if (ret) { /* In case the bin file is partially destroyed. */ if (regbin->ncfgs == 0) pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; break; } offset += (int)fw_hdr->config_size[i]; regbin->ncfgs += 1; } out: if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) { dev_err(pcm_dev->dev, "%s: remove config due to fw load error!\n", __func__); pcmdevice_config_info_remove(pcm_dev); } return ret; } static int pcmdevice_comp_probe(struct snd_soc_component *comp) { struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); struct i2c_adapter *adap = pcm_dev->client->adapter; const struct firmware *fw_entry = NULL; int ret, i, j; mutex_lock(&pcm_dev->codec_lock); pcm_dev->component = comp; for (i = 0; i < pcm_dev->ndev; i++) { for (j = 0; j < 2; j++) { ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); if (ret < 0) goto out; } } if (comp->name_prefix) { /* There's name_prefix defined in DTS. Bin file name will be * name_prefix.bin stores the firmware including register * setting and params for different filters inside chips, it * must be copied into firmware folder. The same types of * pcmdevices sitting on the same i2c bus will be aggregated as * one single codec, all of them share the same bin file. */ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN, "%s.bin", comp->name_prefix); } else { /* There's NO name_prefix defined in DTS. Bin file name will be * device-name[defined in pcmdevice_i2c_id]-i2c-bus_id * [0,1,...,N]-sum[1,...,4]dev.bin stores the firmware * including register setting and params for different filters * inside chips, it must be copied into firmware folder. The * same types of pcmdevices sitting on the same i2c bus will be * aggregated as one single codec, all of them share the same * bin file. */ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN, "%s-i2c-%d-%udev.bin", pcm_dev->dev_name, adap->nr, pcm_dev->ndev); } ret = request_firmware(&fw_entry, pcm_dev->bin_name, pcm_dev->dev); if (ret) { dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, pcm_dev->bin_name, ret); goto out; } ret = pcmdev_regbin_ready(fw_entry, pcm_dev); if (ret) { dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, pcm_dev->bin_name, ret); goto out; } ret = pcmdev_profile_ctrl_add(pcm_dev); out: if (fw_entry) release_firmware(fw_entry); mutex_unlock(&pcm_dev->codec_lock); return ret; } static void pcmdevice_comp_remove(struct snd_soc_component *codec) { struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); if (!pcm_dev) return; mutex_lock(&pcm_dev->codec_lock); pcmdevice_config_info_remove(pcm_dev); mutex_unlock(&pcm_dev->codec_lock); } static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("ASI1 OUT", "ASI1 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_INPUT("MIC"), }; static const struct snd_soc_dapm_route pcmdevice_audio_map[] = { {"OUT", NULL, "ASI"}, {"ASI1 OUT", NULL, "MIC"}, }; static const struct snd_soc_component_driver soc_codec_driver_pcmdevice = { .probe = pcmdevice_comp_probe, .remove = pcmdevice_comp_remove, .dapm_widgets = pcmdevice_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(pcmdevice_dapm_widgets), .dapm_routes = pcmdevice_audio_map, .num_dapm_routes = ARRAY_SIZE(pcmdevice_audio_map), .suspend_bias_off = 1, .idle_bias_on = 0, .use_pmdown_time = 1, .endianness = 1, }; static int pcmdev_single_byte_wr(struct pcmdevice_priv *pcm_dev, unsigned char *data, int devn, int sublocksize) { unsigned short len = get_unaligned_be16(&data[2]); int offset = 2; int i, ret; offset += 2; if (offset + 4 * len > sublocksize) { dev_err(pcm_dev->dev, "%s: dev-%d byt wr out of boundary\n", __func__, devn); return -EINVAL; } for (i = 0; i < len; i++) { ret = pcmdev_dev_write(pcm_dev, devn, PCMDEVICE_REG(data[offset + 1], data[offset + 2]), data[offset + 3]); /* skip this error for next operation or next devices */ if (ret < 0) dev_err(pcm_dev->dev, "%s: dev-%d single write err\n", __func__, devn); offset += 4; } return offset; } static int pcmdev_burst_wr(struct pcmdevice_priv *pcm_dev, unsigned char *data, int devn, int sublocksize) { unsigned short len = get_unaligned_be16(&data[2]); int offset = 2; int ret; offset += 2; if (offset + 4 + len > sublocksize) { dev_err(pcm_dev->dev, "%s: dev-%d burst Out of boundary\n", __func__, devn); return -EINVAL; } if (len % 4) { dev_err(pcm_dev->dev, "%s: dev-%d bst-len(%u) not div by 4\n", __func__, devn, len); return -EINVAL; } ret = pcmdev_dev_bulk_write(pcm_dev, devn, PCMDEVICE_REG(data[offset + 1], data[offset + 2]), &(data[offset + 4]), len); /* skip this error for next devices */ if (ret < 0) dev_err(pcm_dev->dev, "%s: dev-%d bulk_write err = %d\n", __func__, devn, ret); offset += (len + 4); return offset; } static int pcmdev_delay(struct pcmdevice_priv *pcm_dev, unsigned char *data, int devn, int sublocksize) { unsigned int delay_time = 0; int offset = 2; if (offset + 2 > sublocksize) { dev_err(pcm_dev->dev, "%s: dev-%d delay out of boundary\n", __func__, devn); return -EINVAL; } delay_time = get_unaligned_be16(&data[2]) * 1000; usleep_range(delay_time, delay_time + 50); offset += 2; return offset; } static int pcmdev_bits_wr(struct pcmdevice_priv *pcm_dev, unsigned char *data, int devn, int sublocksize) { int offset = 2; int ret; if (offset + 6 > sublocksize) { dev_err(pcm_dev->dev, "%s: dev-%d bit write out of memory\n", __func__, devn); return -EINVAL; } ret = pcmdev_dev_update_bits(pcm_dev, devn, PCMDEVICE_REG(data[offset + 3], data[offset + 4]), data[offset + 1], data[offset + 5]); /* skip this error for next devices */ if (ret < 0) dev_err(pcm_dev->dev, "%s: dev-%d update_bits err = %d\n", __func__, devn, ret); offset += 6; return offset; } static int pcmdevice_process_block(void *ctxt, unsigned char *data, unsigned char dev_idx, int sublocksize) { struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; int devn, dev_end, ret = 0; unsigned char subblk_typ = data[1]; if (dev_idx) { devn = dev_idx - 1; dev_end = dev_idx; } else { devn = 0; dev_end = pcm_dev->ndev; } /* loop in case of several devices sharing the same sub-block */ for (; devn < dev_end; devn++) { switch (subblk_typ) { case PCMDEVICE_CMD_SING_W: ret = pcmdev_single_byte_wr(pcm_dev, data, devn, sublocksize); break; case PCMDEVICE_CMD_BURST: ret = pcmdev_burst_wr(pcm_dev, data, devn, sublocksize); break; case PCMDEVICE_CMD_DELAY: ret = pcmdev_delay(pcm_dev, data, devn, sublocksize); break; case PCMDEVICE_CMD_FIELD_W: ret = pcmdev_bits_wr(pcm_dev, data, devn, sublocksize); break; default: break; } /* * In case of sub-block error, break the loop for the rest of * devices. */ if (ret < 0) break; } return ret; } static void pcmdevice_select_cfg_blk(void *ctxt, int conf_no, unsigned char block_type) { struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; struct pcmdevice_regbin *regbin = &(pcm_dev->regbin); struct pcmdevice_config_info **cfg_info = regbin->cfg_info; struct pcmdevice_block_data **blk_data; int j, k; if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) { dev_err(pcm_dev->dev, "%s: conf_no should be less than %u\n", __func__, regbin->ncfgs); goto out; } blk_data = cfg_info[conf_no]->blk_data; for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { unsigned int length = 0, ret; if (block_type > 5 || block_type < 2) { dev_err(pcm_dev->dev, "%s: block_type should be out of range\n", __func__); goto out; } if (block_type != blk_data[j]->block_type) continue; for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { ret = pcmdevice_process_block(pcm_dev, blk_data[j]->regdata + length, blk_data[j]->dev_idx, blk_data[j]->block_size - length); length += ret; if (blk_data[j]->block_size < length) { dev_err(pcm_dev->dev, "%s: %u %u out of boundary\n", __func__, length, blk_data[j]->block_size); break; } } if (length != blk_data[j]->block_size) dev_err(pcm_dev->dev, "%s: %u %u size is not same\n", __func__, length, blk_data[j]->block_size); } out: return; } static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *codec = dai->component; struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); unsigned char block_type; if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) { dev_err(pcm_dev->dev, "%s: bin file not loaded\n", __func__); return -EINVAL; } if (mute) block_type = PCMDEVICE_BIN_BLK_PRE_SHUTDOWN; else block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; mutex_lock(&pcm_dev->codec_lock); pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); mutex_unlock(&pcm_dev->codec_lock); return 0; } static int pcmdevice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(dai); unsigned int fsrate; unsigned int slot_width; int bclk_rate; int ret = 0; fsrate = params_rate(params); switch (fsrate) { case 48000: break; case 44100: break; default: dev_err(pcm_dev->dev, "%s: incorrect sample rate = %u\n", __func__, fsrate); ret = -EINVAL; goto out; } slot_width = params_width(params); switch (slot_width) { case 16: break; case 20: break; case 24: break; case 32: break; default: dev_err(pcm_dev->dev, "%s: incorrect slot width = %u\n", __func__, slot_width); ret = -EINVAL; goto out; } bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) { dev_err(pcm_dev->dev, "%s: incorrect bclk rate = %d\n", __func__, bclk_rate); ret = bclk_rate; } out: return ret; } static const struct snd_soc_dai_ops pcmdevice_dai_ops = { .mute_stream = pcmdevice_mute, .hw_params = pcmdevice_hw_params, }; static struct snd_soc_dai_driver pcmdevice_dai_driver[] = { { .name = "pcmdevice-codec", .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = PCMDEVICE_MAX_CHANNELS, .rates = PCMDEVICE_RATES, .formats = PCMDEVICE_FORMATS, }, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = PCMDEVICE_MAX_CHANNELS, .rates = PCMDEVICE_RATES, .formats = PCMDEVICE_FORMATS, }, .ops = &pcmdevice_dai_ops, .symmetric_rate = 1, } }; #ifdef CONFIG_OF static const struct of_device_id pcmdevice_of_match[] = { { .compatible = "ti,adc3120" }, { .compatible = "ti,adc5120" }, { .compatible = "ti,adc6120" }, { .compatible = "ti,dix4192" }, { .compatible = "ti,pcm1690" }, { .compatible = "ti,pcm3120" }, { .compatible = "ti,pcm3140" }, { .compatible = "ti,pcm5120" }, { .compatible = "ti,pcm5140" }, { .compatible = "ti,pcm6120" }, { .compatible = "ti,pcm6140" }, { .compatible = "ti,pcm6240" }, { .compatible = "ti,pcm6260" }, { .compatible = "ti,pcm9211" }, { .compatible = "ti,pcmd3140" }, { .compatible = "ti,pcmd3180" }, { .compatible = "ti,pcmd512x" }, { .compatible = "ti,taa5212" }, { .compatible = "ti,taa5412" }, { .compatible = "ti,tad5212" }, { .compatible = "ti,tad5412" }, {}, }; MODULE_DEVICE_TABLE(of, pcmdevice_of_match); #endif static const struct regmap_range_cfg pcmdevice_ranges[] = { { .range_min = 0, .range_max = 256 * 128, .selector_reg = PCMDEVICE_PAGE_SELECT, .selector_mask = 0xff, .selector_shift = 0, .window_start = 0, .window_len = 128, }, }; static const struct regmap_config pcmdevice_i2c_regmap = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_MAPLE, .ranges = pcmdevice_ranges, .num_ranges = ARRAY_SIZE(pcmdevice_ranges), .max_register = 256 * 128, }; static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev) { if (gpio_is_valid(pcm_dev->irq_info.gpio)) { gpio_free(pcm_dev->irq_info.gpio); free_irq(pcm_dev->irq_info.nmb, pcm_dev); } mutex_destroy(&pcm_dev->codec_lock); } static char *str_to_upper(char *str) { char *orig = str; if (!str) return NULL; while (*str) { *str = toupper(*str); str++; } return orig; } static int pcmdevice_i2c_probe(struct i2c_client *i2c) { const struct i2c_device_id *id = i2c_match_id(pcmdevice_i2c_id, i2c); struct pcmdevice_priv *pcm_dev; struct device_node *np; unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES]; int ret = 0, i = 0, ndev = 0; pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); if (!pcm_dev) return -ENOMEM; pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; pcm_dev->dev = &i2c->dev; pcm_dev->client = i2c; if (pcm_dev->chip_id >= MAX_DEVICE) pcm_dev->chip_id = 0; strscpy(pcm_dev->dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name, sizeof(pcm_dev->dev_name)); strscpy(pcm_dev->upper_dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name, sizeof(pcm_dev->upper_dev_name)); str_to_upper(pcm_dev->upper_dev_name); pcm_dev->regmap = devm_regmap_init_i2c(i2c, &pcmdevice_i2c_regmap); if (IS_ERR(pcm_dev->regmap)) { ret = PTR_ERR(pcm_dev->regmap); dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n", __func__, ret); goto out; } i2c_set_clientdata(i2c, pcm_dev); mutex_init(&pcm_dev->codec_lock); np = pcm_dev->dev->of_node; if (IS_ENABLED(CONFIG_OF)) { u64 addr; for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) { if (of_property_read_reg(np, i, &addr, NULL)) break; dev_addrs[ndev++] = addr; } } else { ndev = 1; dev_addrs[0] = i2c->addr; } pcm_dev->irq_info.gpio = of_irq_get(np, 0); for (i = 0; i < ndev; i++) pcm_dev->addr[i] = dev_addrs[i]; pcm_dev->ndev = ndev; pcm_dev->hw_rst = devm_gpiod_get_optional(&i2c->dev, "reset-gpios", GPIOD_OUT_HIGH); /* No reset GPIO, no side-effect */ if (IS_ERR(pcm_dev->hw_rst)) { if (pcm_dev->chip_id == PCM9211 || pcm_dev->chip_id == PCM1690) pcm9211_sw_rst(pcm_dev); else pcmdevice_sw_rst(pcm_dev); } else { gpiod_set_value_cansleep(pcm_dev->hw_rst, 0); usleep_range(500, 1000); gpiod_set_value_cansleep(pcm_dev->hw_rst, 1); } if (pcm_dev->chip_id == PCM1690) goto skip_interrupt; if (gpio_is_valid(pcm_dev->irq_info.gpio)) { dev_dbg(pcm_dev->dev, "irq-gpio = %d", pcm_dev->irq_info.gpio); ret = gpio_request(pcm_dev->irq_info.gpio, "PCMDEV-IRQ"); if (!ret) { int gpio = pcm_dev->irq_info.gpio; gpio_direction_input(gpio); pcm_dev->irq_info.nmb = gpio_to_irq(gpio); } else dev_err(pcm_dev->dev, "%s: GPIO %d request error\n", __func__, pcm_dev->irq_info.gpio); } else dev_err(pcm_dev->dev, "Looking up irq-gpio failed %d\n", pcm_dev->irq_info.gpio); skip_interrupt: ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_driver_pcmdevice, pcmdevice_dai_driver, ARRAY_SIZE(pcmdevice_dai_driver)); if (ret < 0) dev_err(&i2c->dev, "probe register comp failed %d\n", ret); out: if (ret < 0) pcmdevice_remove(pcm_dev); return ret; } static void pcmdevice_i2c_remove(struct i2c_client *i2c) { struct pcmdevice_priv *pcm_dev = i2c_get_clientdata(i2c); pcmdevice_remove(pcm_dev); } static struct i2c_driver pcmdevice_i2c_driver = { .driver = { .name = "pcmdevice-codec", .of_match_table = of_match_ptr(pcmdevice_of_match), }, .probe = pcmdevice_i2c_probe, .remove = pcmdevice_i2c_remove, .id_table = pcmdevice_i2c_id, }; module_i2c_driver(pcmdevice_i2c_driver); MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>"); MODULE_DESCRIPTION("ASoC PCM6240 Family Audio ADC/DAC Driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1