1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038 |
- /* Copyright (c) 2009-2013, 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.
- *
- */
- /*
- * Qualcomm Tavarua FM core driver
- */
- /* driver definitions */
- #define DRIVER_AUTHOR "Qualcomm"
- #define DRIVER_NAME "radio-tavarua"
- #define DRIVER_CARD "Qualcomm FM Radio Transceiver"
- #define DRIVER_DESC "I2C radio driver for Qualcomm FM Radio Transceiver "
- #define DRIVER_VERSION "1.0.0"
- #include <linux/version.h>
- #include <linux/init.h> /* Initdata */
- #include <linux/delay.h> /* udelay */
- #include <linux/uaccess.h> /* copy to/from user */
- #include <linux/kfifo.h> /* lock free circular buffer */
- #include <linux/param.h>
- #include <linux/i2c.h>
- #include <linux/irq.h>
- #include <linux/interrupt.h>
- /* kernel includes */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/version.h>
- #include <linux/videodev2.h>
- #include <linux/mutex.h>
- #include <media/v4l2-common.h>
- #include <asm/unaligned.h>
- #include <media/v4l2-ioctl.h>
- #include <linux/unistd.h>
- #include <asm/atomic.h>
- #include <media/tavarua.h>
- #include <linux/mfd/marimba.h>
- #include <linux/platform_device.h>
- #include <linux/workqueue.h>
- #include <linux/slab.h>
- /*
- regional parameters for radio device
- */
- struct region_params_t {
- enum tavarua_region_t region;
- unsigned int band_high;
- unsigned int band_low;
- char emphasis;
- char rds_std;
- char spacing;
- };
- struct srch_params_t {
- unsigned short srch_pi;
- unsigned char srch_pty;
- unsigned int preset_num;
- int get_list;
- };
- /* Main radio device structure,
- acts as a shadow copy of the
- actual tavaura registers */
- struct tavarua_device {
- struct video_device *videodev;
- /* driver management */
- atomic_t users;
- /* top level driver data */
- struct marimba *marimba;
- struct device *dev;
- /* platform specific functionality */
- struct marimba_fm_platform_data *pdata;
- unsigned int chipID;
- /*RDS buffers + Radio event buffer*/
- struct kfifo data_buf[TAVARUA_BUF_MAX];
- /* search paramters */
- struct srch_params_t srch_params;
- /* keep track of pending xfrs */
- int pending_xfrs[TAVARUA_XFR_MAX];
- int xfr_bytes_left;
- int xfr_in_progress;
- /* Transmit data */
- enum tavarua_xfr_ctrl_t tx_mode;
- /* synchrnous xfr data */
- unsigned char sync_xfr_regs[XFR_REG_NUM];
- struct completion sync_xfr_start;
- struct completion shutdown_done;
- struct completion sync_req_done;
- int tune_req;
- /* internal register status */
- unsigned char registers[RADIO_REGISTERS];
- /* regional settings */
- struct region_params_t region_params;
- /* power mode */
- int lp_mode;
- int handle_irq;
- /* global lock */
- struct mutex lock;
- /* buffer locks*/
- spinlock_t buf_lock[TAVARUA_BUF_MAX];
- /* work queue */
- struct workqueue_struct *wqueue;
- struct delayed_work work;
- /* wait queue for blocking event read */
- wait_queue_head_t event_queue;
- /* wait queue for raw rds read */
- wait_queue_head_t read_queue;
- /* PTY for FM Tx */
- int pty;
- /* PI for FM TX */
- int pi;
- /*PS repeatcount for PS Tx */
- int ps_repeatcount;
- int enable_optimized_srch_alg;
- unsigned char spur_table_size;
- struct fm_spur_data spur_data;
- atomic_t validate_channel;
- unsigned char is_station_valid;
- };
- /**************************************************************************
- * Module Parameters
- **************************************************************************/
- /* Radio Nr */
- static int radio_nr = -1;
- module_param(radio_nr, int, 0);
- MODULE_PARM_DESC(radio_nr, "Radio Nr");
- static int wait_timeout = WAIT_TIMEOUT;
- /* Bahama's version*/
- static u8 bahama_version;
- /* RDS buffer blocks */
- static unsigned int rds_buf = 100;
- module_param(rds_buf, uint, 0);
- MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
- /* static variables */
- static struct tavarua_device *private_data;
- /* forward declerations */
- static int tavarua_disable_interrupts(struct tavarua_device *radio);
- static int tavarua_setup_interrupts(struct tavarua_device *radio,
- enum radio_state_t state);
- static int tavarua_start(struct tavarua_device *radio,
- enum radio_state_t state);
- static int tavarua_request_irq(struct tavarua_device *radio);
- static void start_pending_xfr(struct tavarua_device *radio);
- static int update_spur_table(struct tavarua_device *radio);
- static int xfr_rdwr_data(struct tavarua_device *radio, int op, int size,
- unsigned long offset, unsigned char *buf);
- static int compute_MPX_DCC(struct tavarua_device *radio, int *val);
- /* work function */
- static void read_int_stat(struct work_struct *work);
- static int is_bahama(void)
- {
- int id = 0;
- switch (id = adie_get_detected_connectivity_type()) {
- case BAHAMA_ID:
- FMDBG("It is Bahama\n");
- return 1;
- case MARIMBA_ID:
- FMDBG("It is Marimba\n");
- return 0;
- default:
- printk(KERN_ERR "%s: unexpected adie connectivity type: %d\n",
- __func__, id);
- return -ENODEV;
- }
- }
- static int set_fm_slave_id(struct tavarua_device *radio)
- {
- int bahama_present = is_bahama();
- if (bahama_present == -ENODEV)
- return -ENODEV;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (bahama_present)
- radio->marimba->mod_id = SLAVE_ID_BAHAMA_FM;
- else
- radio->marimba->mod_id = MARIMBA_SLAVE_ID_FM;
- return 0;
- }
- /*=============================================================================
- FUNCTION: tavarua_isr
- =============================================================================*/
- /**
- This function is called when GPIO is toggled. This functions queues the event
- to interrupt queue, which is later handled by isr handling funcion.
- i.e. INIT_DELAYED_WORK(&radio->work, read_int_stat);
- @param irq: irq that is toggled.
- @param dev_id: structure pointer passed by client.
- @return IRQ_HANDLED.
- */
- static irqreturn_t tavarua_isr(int irq, void *dev_id)
- {
- struct tavarua_device *radio = dev_id;
- /* schedule a tasklet to handle host intr */
- /* The call to queue_delayed_work ensures that a minimum delay (in jiffies)
- * passes before the work is actually executed. The return value from the
- * function is nonzero if the work_struct was actually added to queue
- * (otherwise, it may have already been there and will not be added a second
- * time).
- */
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- queue_delayed_work(radio->wqueue, &radio->work,
- msecs_to_jiffies(TAVARUA_DELAY));
- return IRQ_HANDLED;
- }
- /**************************************************************************
- * Interface to radio internal registers over top level marimba driver
- *************************************************************************/
- /*=============================================================================
- FUNCTION: tavarua_read_registers
- =============================================================================*/
- /**
- This function is called to read a number of bytes from an I2C interface.
- The bytes read are stored in internal register status (shadow copy).
- @param radio: structure pointer passed by client.
- @param offset: register offset.
- @param len: num of bytes.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_read_registers(struct tavarua_device *radio,
- unsigned char offset, int len)
- {
- int retval = 0, i = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- retval = set_fm_slave_id(radio);
- if (retval == -ENODEV)
- return retval;
- FMDBG_I2C("I2C Slave: %x, Read Offset(%x): Data [",
- radio->marimba->mod_id,
- offset);
- retval = marimba_read(radio->marimba, offset,
- &radio->registers[offset], len);
- if (retval > 0) {
- for (i = 0; i < len; i++)
- FMDBG_I2C("%02x ", radio->registers[offset+i]);
- FMDBG_I2C(" ]\n");
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_write_register
- =============================================================================*/
- /**
- This function is called to write a byte over the I2C interface.
- The corresponding shadow copy is stored in internal register status.
- @param radio: structure pointer passed by client.
- @param offset: register offset.
- @param value: buffer to be written to the registers.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_write_register(struct tavarua_device *radio,
- unsigned char offset, unsigned char value)
- {
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- retval = set_fm_slave_id(radio);
- if (retval == -ENODEV)
- return retval;
- FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
- radio->marimba->mod_id,
- offset);
- retval = marimba_write(radio->marimba, offset, &value, 1);
- if (retval > 0) {
- if (offset < RADIO_REGISTERS) {
- radio->registers[offset] = value;
- FMDBG_I2C("%02x ", radio->registers[offset]);
- }
- FMDBG_I2C(" ]\n");
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_write_registers
- =============================================================================*/
- /**
- This function is called to write a number of bytes over the I2C interface.
- The corresponding shadow copy is stored in internal register status.
- @param radio: structure pointer passed by client.
- @param offset: register offset.
- @param buf: buffer to be written to the registers.
- @param len: num of bytes.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_write_registers(struct tavarua_device *radio,
- unsigned char offset, unsigned char *buf, int len)
- {
- int i;
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- retval = set_fm_slave_id(radio);
- if (retval == -ENODEV)
- return retval;
- FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
- radio->marimba->mod_id,
- offset);
- retval = marimba_write(radio->marimba, offset, buf, len);
- if (retval > 0) { /* if write successful, update internal state too */
- for (i = 0; i < len; i++) {
- if ((offset+i) < RADIO_REGISTERS) {
- radio->registers[offset+i] = buf[i];
- FMDBG_I2C("%x ", radio->registers[offset+i]);
- }
- }
- FMDBG_I2C(" ]\n");
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: read_data_blocks
- =============================================================================*/
- /**
- This function reads Raw RDS blocks from Core regs to driver
- internal regs (shadow copy).
- @param radio: structure pointer passed by client.
- @param offset: register offset.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int read_data_blocks(struct tavarua_device *radio, unsigned char offset)
- {
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- /* read all 3 RDS blocks */
- return tavarua_read_registers(radio, offset, RDS_BLOCK*4);
- }
- /*=============================================================================
- FUNCTION: tavarua_rds_read
- =============================================================================*/
- /**
- This is a rds processing function reads that reads Raw RDS blocks from Core
- regs to driver internal regs (shadow copy). It then fills the V4L2 RDS buffer,
- which is read by App using JNI interface.
- @param radio: structure pointer passed by client.
- @return None.
- */
- static void tavarua_rds_read(struct tavarua_device *radio)
- {
- struct kfifo *rds_buf;
- unsigned char blocknum;
- unsigned char tmp[3];
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
- if (read_data_blocks(radio, RAW_RDS) < 0)
- return;
- /* copy all four RDS blocks to internal buffer */
- for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
- /* Fill the V4L2 RDS buffer */
- put_unaligned(cpu_to_le16(radio->registers[RAW_RDS +
- blocknum*RDS_BLOCK]), (unsigned short *) tmp);
- tmp[2] = blocknum; /* offset name */
- tmp[2] |= blocknum << 3; /* received offset */
- tmp[2] |= 0x40; /* corrected error(s) */
- /* copy RDS block to internal buffer */
- kfifo_in_locked(rds_buf, tmp, 3, &radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
- }
- /* wake up read queue */
- if (kfifo_len(rds_buf))
- wake_up_interruptible(&radio->read_queue);
- }
- /*=============================================================================
- FUNCTION: request_read_xfr
- =============================================================================*/
- /**
- This function sets the desired MODE in the XFRCTRL register and also sets the
- CTRL field to read.
- This is an asynchronous way of reading the XFR registers. Client would request
- by setting the desired mode in the XFRCTRL register and then would initiate
- the actual data register read by calling copy_from_xfr up on SOC signals
- success.
- NOTE:
- The Data Transfer (XFR) registers are used to pass various data and
- configuration parameters between the Core and host processor.
- To read from the XFR registers, the host processor must set the desired MODE
- in the XFRCTRL register and set the CTRL field to read. The Core will then
- populate the XFRDAT0 - XFRDAT15 registers with the defined mode bytes. The
- Core will set the TRANSFER interrupt status bit and interrupt the host if the
- TRANSFERCTRL interrupt control bit is set. The host can then extract the XFR
- mode bytes once it detects that the Core has updated the registers.
- @param radio: structure pointer passed by client.
- @return Always returns 0.
- */
- static int request_read_xfr(struct tavarua_device *radio,
- enum tavarua_xfr_ctrl_t mode){
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- tavarua_write_register(radio, XFRCTRL, mode);
- msleep(TAVARUA_DELAY);
- return 0;
- }
- /*=============================================================================
- FUNCTION: copy_from_xfr
- =============================================================================*/
- /**
- This function is used to read XFR mode bytes once it detects that the Core
- has updated the registers. It also updates XFR regs to the appropriate
- internal buffer n bytes.
- NOTE:
- This function should be used in conjuction with request_read_xfr. Refer
- request_read_xfr for XFR mode transaction details.
- @param radio: structure pointer passed by client.
- @param buf_type: Index into RDS/Radio event buffer to use.
- @param len: num of bytes.
- @return Always returns 0.
- */
- static int copy_from_xfr(struct tavarua_device *radio,
- enum tavarua_buf_t buf_type, unsigned int n){
- struct kfifo *data_fifo;
- unsigned char *xfr_regs;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- data_fifo = &radio->data_buf[buf_type];
- xfr_regs = &radio->registers[XFRCTRL+1];
- kfifo_in_locked(data_fifo, xfr_regs, n, &radio->buf_lock[buf_type]);
- return 0;
- }
- /*=============================================================================
- FUNCTION: write_to_xfr
- =============================================================================*/
- /**
- This function sets the desired MODE in the XFRCTRL register and it also sets
- the CTRL field and data to write.
- This also writes all the XFRDATx registers with the desired input buffer.
- NOTE:
- The Data Transfer (XFR) registers are used to pass various data and
- configuration parameters between the Core and host processor.
- To write data to the Core, the host processor updates XFRDAT0 - XFRDAT15 with
- the appropriate mode bytes. The host processor must then set the desired MODE
- in the XFRCTRL register and set the CTRL field to write. The core will detect
- that the XFRCTRL register was written to and will read the XFR mode bytes.
- After reading all the mode bytes, the Core will set the TRANSFER interrupt
- status bit and interrupt the host if the TRANSFERCTRL interrupt control bit
- is set.
- @param radio: structure pointer passed by client.
- @param mode: XFR mode to write in XFRCTRL register.
- @param buf: buffer to be written to the registers.
- @param len: num of bytes.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int write_to_xfr(struct tavarua_device *radio, unsigned char mode,
- char *buf, int len)
- {
- char buffer[len+1];
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- memcpy(buffer+1, buf, len);
- /* buffer[0] corresponds to XFRCTRL register
- set the CTRL bit to 1 for write mode
- */
- buffer[0] = ((1<<7) | mode);
- return tavarua_write_registers(radio, XFRCTRL, buffer, sizeof(buffer));
- }
- /*=============================================================================
- FUNCTION: xfr_intf_own
- =============================================================================*/
- /**
- This function is used to check if there is any pending XFR mode operation.
- If yes, wait for it to complete, else update the flag to indicate XFR
- operation is in progress
- @param radio: structure pointer passed by client.
- @return 0 on success.
- -ETIME on timeout.
- */
- static int xfr_intf_own(struct tavarua_device *radio)
- {
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- mutex_lock(&radio->lock);
- if (radio->xfr_in_progress) {
- radio->pending_xfrs[TAVARUA_XFR_SYNC] = 1;
- mutex_unlock(&radio->lock);
- if (!wait_for_completion_timeout(&radio->sync_xfr_start,
- msecs_to_jiffies(wait_timeout)))
- return -ETIME;
- } else {
- FMDBG("gained ownership of xfr\n");
- radio->xfr_in_progress = 1;
- mutex_unlock(&radio->lock);
- }
- return 0;
- }
- /*=============================================================================
- FUNCTION: sync_read_xfr
- =============================================================================*/
- /**
- This function is used to do synchronous XFR read operation.
- @param radio: structure pointer passed by client.
- @param xfr_type: XFR mode to write in XFRCTRL register.
- @param buf: buffer to be read from the core.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int sync_read_xfr(struct tavarua_device *radio,
- enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
- {
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- retval = xfr_intf_own(radio);
- if (retval < 0)
- return retval;
- retval = tavarua_write_register(radio, XFRCTRL, xfr_type);
- if (retval >= 0) {
- /* Wait for interrupt i.e. complete
- (&radio->sync_req_done); call */
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
- retval = -ETIME;
- } else {
- memcpy(buf, radio->sync_xfr_regs, XFR_REG_NUM);
- }
- }
- radio->xfr_in_progress = 0;
- start_pending_xfr(radio);
- FMDBG("%s: %d\n", __func__, retval);
- return retval;
- }
- /*=============================================================================
- FUNCTION: sync_write_xfr
- =============================================================================*/
- /**
- This function is used to do synchronous XFR write operation.
- @param radio: structure pointer passed by client.
- @param xfr_type: XFR mode to write in XFRCTRL register.
- @param buf: buffer to be written to the core.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int sync_write_xfr(struct tavarua_device *radio,
- enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
- {
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(buf == NULL)) {
- FMDERR("%s:buf is null", __func__);
- return -EINVAL;
- }
- retval = xfr_intf_own(radio);
- if (retval < 0)
- return retval;
- retval = write_to_xfr(radio, xfr_type, buf, XFR_REG_NUM);
- if (retval >= 0) {
- /* Wait for interrupt i.e. complete
- (&radio->sync_req_done); call */
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
- FMDBG("Write xfr timeout");
- }
- }
- radio->xfr_in_progress = 0;
- start_pending_xfr(radio);
- FMDBG("%s: %d\n", __func__, retval);
- return retval;
- }
- /*=============================================================================
- FUNCTION: start_pending_xfr
- =============================================================================*/
- /**
- This function checks if their are any pending xfr interrupts and if
- the interrupts are either RDS PS, RDS RT, RDS AF, SCANNEXT, SEARCH or SYNC
- then initiates corresponding read operation. Preference is given to RAW RDS
- data (SYNC) over processed data (PS, RT, AF, etc) from core.
- @param radio: structure pointer passed by client.
- @return None.
- */
- static void start_pending_xfr(struct tavarua_device *radio)
- {
- int i;
- enum tavarua_xfr_t xfr;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- for (i = 0; i < TAVARUA_XFR_MAX; i++) {
- if (radio->pending_xfrs[i]) {
- radio->xfr_in_progress = 1;
- xfr = (enum tavarua_xfr_t)i;
- switch (xfr) {
- /* priority given to synchronous xfrs */
- case TAVARUA_XFR_SYNC:
- complete(&radio->sync_xfr_start);
- break;
- /* asynchrnous xfrs */
- case TAVARUA_XFR_SRCH_LIST:
- request_read_xfr(radio, RX_STATIONS_0);
- break;
- case TAVARUA_XFR_RT_RDS:
- request_read_xfr(radio, RDS_RT_0);
- break;
- case TAVARUA_XFR_PS_RDS:
- request_read_xfr(radio, RDS_PS_0);
- break;
- case TAVARUA_XFR_AF_LIST:
- request_read_xfr(radio, RDS_AF_0);
- break;
- default:
- FMDERR("%s: Unsupported XFR %d\n",
- __func__, xfr);
- }
- radio->pending_xfrs[i] = 0;
- FMDBG("resurrect xfr %d\n", i);
- }
- }
- return;
- }
- /*=============================================================================
- FUNCTION: tavarua_q_event
- =============================================================================*/
- /**
- This function is called to queue an event for user.
- NOTE:
- Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or
- filled (output) buffer in the driver's incoming queue.
- Pleaes refer tavarua_probe where we register different ioctl's for FM.
- @param radio: structure pointer passed by client.
- @param event: event to be queued.
- @return None.
- */
- static void tavarua_q_event(struct tavarua_device *radio,
- enum tavarua_evt_t event)
- {
- struct kfifo *data_b;
- unsigned char evt = event;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- data_b = &radio->data_buf[TAVARUA_BUF_EVENTS];
- FMDBG("updating event_q with event %x\n", event);
- if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[TAVARUA_BUF_EVENTS]))
- wake_up_interruptible(&radio->event_queue);
- }
- /*=============================================================================
- FUNCTION: tavarua_start_xfr
- =============================================================================*/
- /**
- This function is called to process interrupts which require multiple XFR
- operations (RDS search, RDS PS, RDS RT, etc). if any XFR operation is
- already in progress we store information about pending interrupt, which
- will be processed in future when current pending operation is done.
- @param radio: structure pointer passed by client.
- @param pending_id: XFR operation (which requires multiple XFR operations in
- steps) to start.
- @param xfr_id: XFR mode to write in XFRCTRL register.
- @return None.
- */
- static void tavarua_start_xfr(struct tavarua_device *radio,
- enum tavarua_xfr_t pending_id, enum tavarua_xfr_ctrl_t xfr_id)
- {
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- if (radio->xfr_in_progress)
- radio->pending_xfrs[pending_id] = 1;
- else {
- radio->xfr_in_progress = 1;
- request_read_xfr(radio, xfr_id);
- }
- }
- /*=============================================================================
- FUNCTION: tavarua_handle_interrupts
- =============================================================================*/
- /**
- This function processes the interrupts.
- NOTE:
- tavarua_q_event is used to queue events in App buffer. i.e. App calls the
- VIDIOC_QBUF ioctl to enqueue an empty (capturing) buffer, which is filled
- by tavarua_q_event call.
- Any async event that requires multiple steps, i.e. search, RT, PS, etc is
- handled one at a time. (We preserve other interrupts when processing one).
- Sync interrupts are given priority.
- @param radio: structure pointer passed by client.
- @return None.
- */
- static void tavarua_handle_interrupts(struct tavarua_device *radio)
- {
- int i;
- int retval, adj_channel_tune_req = 0;
- unsigned char xfr_status;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- if (!radio->handle_irq) {
- FMDBG("IRQ happend, but I wont handle it\n");
- return;
- }
- mutex_lock(&radio->lock);
- retval = tavarua_read_registers(radio, STATUS_REG1, STATUS_REG_NUM);
- if (retval < 0) {
- FMDERR("Fails to read status register and try once again");
- msleep(TAVARUA_DELAY);
- retval = tavarua_read_registers(radio, STATUS_REG1,
- STATUS_REG_NUM);
- if (retval < 0)
- FMDERR("Fails to read status register");
- }
- FMDBG("INTSTAT1 <%x>\n", radio->registers[STATUS_REG1]);
- FMDBG("INTSTAT2 <%x>\n", radio->registers[STATUS_REG2]);
- FMDBG("INTSTAT3 <%x>\n", radio->registers[STATUS_REG3]);
- if (radio->registers[STATUS_REG1] & READY) {
- complete(&radio->sync_req_done);
- tavarua_q_event(radio, TAVARUA_EVT_RADIO_READY);
- }
- /* Tune completed */
- if (radio->registers[STATUS_REG1] & TUNE) {
- if (radio->tune_req) {
- complete(&radio->sync_req_done);
- radio->tune_req = 0;
- }
- /*
- * Do not queue the TUNE event while validating if the station
- * is good or not. As part of channel validation we tune to the
- * adjacent station, measure its MPX_DCC value, then tune back
- * to the original station and measure its MPX_DCC value.
- * Compare the MPX_DCC values of curent and adjacent stations
- * and decide if the channel is valid or not. During this period
- * we should not queue the TUNE event to the upper layers.
- */
- adj_channel_tune_req = atomic_read(&radio->validate_channel);
- if (adj_channel_tune_req) {
- complete(&radio->sync_req_done);
- FMDBG("Tune event for adjacent channel\n");
- } else {
- tavarua_q_event(radio, TAVARUA_EVT_TUNE_SUCC);
- FMDBG("Queueing Tune event\n");
- }
- if (radio->srch_params.get_list) {
- tavarua_start_xfr(radio, TAVARUA_XFR_SRCH_LIST,
- RX_STATIONS_0);
- }
- radio->srch_params.get_list = 0;
- radio->xfr_in_progress = 0;
- radio->xfr_bytes_left = 0;
- for (i = 0; i < TAVARUA_BUF_MAX; i++) {
- if (i >= TAVARUA_BUF_RT_RDS)
- kfifo_reset(&radio->data_buf[i]);
- }
- for (i = 0; i < TAVARUA_XFR_MAX; i++) {
- if (i >= TAVARUA_XFR_RT_RDS)
- radio->pending_xfrs[i] = 0;
- }
- retval = tavarua_read_registers(radio, TUNECTRL, 1);
- /* send to user station parameters */
- if (retval > -1) {
- /* Signal strength */
- if (!(radio->registers[TUNECTRL] & SIGSTATE))
- tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
- else
- tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
- /* mono/stereo */
- if ((radio->registers[TUNECTRL] & MOSTSTATE))
- tavarua_q_event(radio, TAVARUA_EVT_STEREO);
- else
- tavarua_q_event(radio, TAVARUA_EVT_MONO);
- /* is RDS available */
- if ((radio->registers[TUNECTRL] & RDSSYNC))
- tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
- else
- tavarua_q_event(radio,
- TAVARUA_EVT_RDS_NOT_AVAIL);
- }
- } else {
- if (radio->tune_req) {
- FMDERR("Tune INT is pending\n");
- mutex_unlock(&radio->lock);
- return;
- }
- }
- /* Search completed (read FREQ) */
- if (radio->registers[STATUS_REG1] & SEARCH)
- tavarua_q_event(radio, TAVARUA_EVT_SEEK_COMPLETE);
- /* Scanning for next station */
- if (radio->registers[STATUS_REG1] & SCANNEXT)
- tavarua_q_event(radio, TAVARUA_EVT_SCAN_NEXT);
- /* Signal indicator change (read SIGSTATE) */
- if (radio->registers[STATUS_REG1] & SIGNAL) {
- retval = tavarua_read_registers(radio, TUNECTRL, 1);
- if (retval > -1) {
- if (!(radio->registers[TUNECTRL] & SIGSTATE))
- tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
- else
- tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
- }
- }
- /* RDS synchronization state change (read RDSSYNC) */
- if (radio->registers[STATUS_REG1] & SYNC) {
- retval = tavarua_read_registers(radio, TUNECTRL, 1);
- if (retval > -1) {
- if ((radio->registers[TUNECTRL] & RDSSYNC))
- tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
- else
- tavarua_q_event(radio,
- TAVARUA_EVT_RDS_NOT_AVAIL);
- }
- }
- /* Audio Control indicator (read AUDIOIND) */
- if (radio->registers[STATUS_REG1] & AUDIO) {
- retval = tavarua_read_registers(radio, AUDIOIND, 1);
- if (retval > -1) {
- if ((radio->registers[AUDIOIND] & 0x01))
- tavarua_q_event(radio, TAVARUA_EVT_STEREO);
- else
- tavarua_q_event(radio, TAVARUA_EVT_MONO);
- }
- }
- /* interrupt register 2 */
- /* New unread RDS data group available */
- if (radio->registers[STATUS_REG2] & RDSDAT) {
- FMDBG("Raw RDS Available\n");
- tavarua_rds_read(radio);
- tavarua_q_event(radio, TAVARUA_EVT_NEW_RAW_RDS);
- }
- /* New RDS Program Service Table available */
- if (radio->registers[STATUS_REG2] & RDSPS) {
- FMDBG("New PS RDS\n");
- tavarua_start_xfr(radio, TAVARUA_XFR_PS_RDS, RDS_PS_0);
- }
- /* New RDS Radio Text available */
- if (radio->registers[STATUS_REG2] & RDSRT) {
- FMDBG("New RT RDS\n");
- tavarua_start_xfr(radio, TAVARUA_XFR_RT_RDS, RDS_RT_0);
- }
- /* New RDS Radio Text available */
- if (radio->registers[STATUS_REG2] & RDSAF) {
- FMDBG("New AF RDS\n");
- tavarua_start_xfr(radio, TAVARUA_XFR_AF_LIST, RDS_AF_0);
- }
- /* Trasmitter an RDS Group */
- if (radio->registers[STATUS_REG2] & TXRDSDAT) {
- FMDBG("New TXRDSDAT\n");
- tavarua_q_event(radio, TAVARUA_EVT_TXRDSDAT);
- }
- /* Complete RDS buffer is available for transmission */
- if (radio->registers[STATUS_REG2] & TXRDSDONE) {
- FMDBG("New TXRDSDAT\n");
- tavarua_q_event(radio, TAVARUA_EVT_TXRDSDONE);
- }
- /* interrupt register 3 */
- /* Data transfer (XFR) completed */
- if (radio->registers[STATUS_REG3] & TRANSFER) {
- FMDBG("XFR Interrupt\n");
- tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
- FMDBG("XFRCTRL IS: %x\n", radio->registers[XFRCTRL]);
- xfr_status = radio->registers[XFRCTRL];
- switch (xfr_status) {
- case RDS_PS_0:
- FMDBG("PS Header\n");
- copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, 5);
- radio->xfr_bytes_left = (radio->registers[XFRCTRL+1] &
- 0x0F) * 8;
- FMDBG("PS RDS Length: %d\n", radio->xfr_bytes_left);
- if ((radio->xfr_bytes_left > 0) &&
- (radio->xfr_bytes_left < 97))
- request_read_xfr(radio, RDS_PS_1);
- else
- radio->xfr_in_progress = 0;
- break;
- case RDS_PS_1:
- case RDS_PS_2:
- case RDS_PS_3:
- case RDS_PS_4:
- case RDS_PS_5:
- case RDS_PS_6:
- FMDBG("PS Data\n");
- copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, XFR_REG_NUM);
- radio->xfr_bytes_left -= XFR_REG_NUM;
- if (radio->xfr_bytes_left > 0) {
- if ((xfr_status + 1) > RDS_PS_6)
- request_read_xfr(radio, RDS_PS_6);
- else
- request_read_xfr(radio, xfr_status+1);
- } else {
- radio->xfr_in_progress = 0;
- tavarua_q_event(radio, TAVARUA_EVT_NEW_PS_RDS);
- }
- break;
- case RDS_RT_0:
- FMDBG("RT Header\n");
- copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, 5);
- radio->xfr_bytes_left = radio->registers[XFRCTRL+1]
- & 0x7F;
- FMDBG("RT RDS Length: %d\n", radio->xfr_bytes_left);
- /*RT_1 to RT_4 16 byte registers so 64 bytes */
- if ((radio->xfr_bytes_left > 0)
- && (radio->xfr_bytes_left < 65))
- request_read_xfr(radio, RDS_RT_1);
- break;
- case RDS_RT_1:
- case RDS_RT_2:
- case RDS_RT_3:
- case RDS_RT_4:
- FMDBG("xfr interrupt RT data\n");
- copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, XFR_REG_NUM);
- radio->xfr_bytes_left -= XFR_REG_NUM;
- if (radio->xfr_bytes_left > 0)
- request_read_xfr(radio, xfr_status+1);
- else {
- radio->xfr_in_progress = 0;
- tavarua_q_event(radio, TAVARUA_EVT_NEW_RT_RDS);
- }
- break;
- case RDS_AF_0:
- copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
- XFR_REG_NUM);
- radio->xfr_bytes_left = radio->registers[XFRCTRL+5]-11;
- if (radio->xfr_bytes_left > 0)
- request_read_xfr(radio, RDS_AF_1);
- else
- radio->xfr_in_progress = 0;
- break;
- case RDS_AF_1:
- copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
- radio->xfr_bytes_left);
- tavarua_q_event(radio, TAVARUA_EVT_NEW_AF_LIST);
- radio->xfr_in_progress = 0;
- break;
- case RX_CONFIG:
- case RADIO_CONFIG:
- case RDS_CONFIG:
- memcpy(radio->sync_xfr_regs,
- &radio->registers[XFRCTRL+1], XFR_REG_NUM);
- complete(&radio->sync_req_done);
- break;
- case RX_STATIONS_0:
- FMDBG("Search list has %d stations\n",
- radio->registers[XFRCTRL+1]);
- radio->xfr_bytes_left = radio->registers[XFRCTRL+1]*2;
- if (!radio->registers[XFRCTRL+1]) {
- copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
- 1);
- tavarua_q_event(radio,
- TAVARUA_EVT_NEW_SRCH_LIST);
- radio->xfr_in_progress = 0;
- } else if (radio->xfr_bytes_left > 14) {
- copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
- RX_STATIONS0_LEN);
- request_read_xfr(radio, RX_STATIONS_1);
- } else if (radio->xfr_bytes_left) {
- FMDBG("In else RX_STATIONS_0\n");
- copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
- radio->xfr_bytes_left+1);
- tavarua_q_event(radio,
- TAVARUA_EVT_NEW_SRCH_LIST);
- radio->xfr_in_progress = 0;
- }
- break;
- case RX_STATIONS_1:
- FMDBG("In RX_STATIONS_1");
- copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
- radio->xfr_bytes_left);
- tavarua_q_event(radio, TAVARUA_EVT_NEW_SRCH_LIST);
- radio->xfr_in_progress = 0;
- break;
- case PHY_TXGAIN:
- FMDBG("read PHY_TXGAIN is successful");
- complete(&radio->sync_req_done);
- break;
- case (XFR_EXT | 0x80):
- FMDBG("Set tone generator successful\n");
- complete(&radio->sync_req_done);
- break;
- case (0x80 | RX_CONFIG):
- case (0x80 | RADIO_CONFIG):
- case (0x80 | RDS_CONFIG):
- case (0x80 | INT_CTRL):
- complete(&radio->sync_req_done);
- break;
- case (0x80 | RDS_RT_0):
- FMDBG("RT Header Sent\n");
- complete(&radio->sync_req_done);
- break;
- case (0x80 | RDS_RT_1):
- case (0x80 | RDS_RT_2):
- case (0x80 | RDS_RT_3):
- case (0x80 | RDS_RT_4):
- FMDBG("xfr interrupt RT data Sent\n");
- complete(&radio->sync_req_done);
- break;
- /*TX Specific transfer */
- case (0x80 | RDS_PS_0):
- FMDBG("PS Header Sent\n");
- complete(&radio->sync_req_done);
- break;
- case (0x80 | RDS_PS_1):
- case (0x80 | RDS_PS_2):
- case (0x80 | RDS_PS_3):
- case (0x80 | RDS_PS_4):
- case (0x80 | RDS_PS_5):
- case (0x80 | RDS_PS_6):
- FMDBG("xfr interrupt PS data Sent\n");
- complete(&radio->sync_req_done);
- break;
- case (0x80 | PHY_TXGAIN):
- FMDBG("write PHY_TXGAIN is successful");
- complete(&radio->sync_req_done);
- break;
- case (XFR_POKE_COMPLETE | LSH_DATA(ONE_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(TWO_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(THREE_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(FOUR_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(FIVE_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(SIX_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(SEVEN_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(EIGHT_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(NINE_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(TEN_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(ELEVEN_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(TWELVE_BYTE, 1)):
- case (XFR_POKE_COMPLETE | LSH_DATA(THIRTEEN_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(ONE_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(TWO_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(THREE_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(FOUR_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(FIVE_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(SIX_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(SEVEN_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(EIGHT_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(NINE_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(TEN_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(ELEVEN_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(TWELVE_BYTE, 1)):
- case (XFR_PEEK_COMPLETE | LSH_DATA(THIRTEEN_BYTE, 1)):
- FMDBG("XFR interrupt for PEEK/POKE complete\n");
- complete(&radio->sync_req_done);
- break;
- default:
- FMDERR("UNKNOWN XFR = %d\n", xfr_status);
- }
- if (!radio->xfr_in_progress)
- start_pending_xfr(radio);
- }
- /* Error occurred. Read ERRCODE to determine cause */
- if (radio->registers[STATUS_REG3] & ERROR) {
- #ifdef FM_DEBUG
- unsigned char xfr_buf[XFR_REG_NUM];
- int retval = sync_read_xfr(radio, ERROR_CODE, xfr_buf);
- FMDBG("retval of ERROR_CODE read : %d\n", retval);
- #endif
- FMDERR("ERROR STATE\n");
- }
- mutex_unlock(&radio->lock);
- FMDBG("Work is done\n");
- }
- /*=============================================================================
- FUNCTION: read_int_stat
- =============================================================================*/
- /**
- This function is scheduled whenever there is an interrupt pending in interrupt
- queue. i.e. kfmradio.
- Whenever there is a GPIO interrupt, a delayed work will be queued in to the
- 'kfmradio' work queue. Upon execution of this work in the queue, a a call
- to read_int_stat function will be made , which would in turn handle the
- interrupts by reading the INTSTATx registers.
- NOTE:
- Tasks to be run out of a workqueue need to be packaged in a struct
- work_struct structure.
- @param work: work_struct structure.
- @return None.
- */
- static void read_int_stat(struct work_struct *work)
- {
- struct tavarua_device *radio;
- if (unlikely(work == NULL)) {
- FMDERR("%s:work is null", __func__);
- return;
- }
- radio = container_of(work, struct tavarua_device, work.work);
- tavarua_handle_interrupts(radio);
- }
- static void fm_shutdown(struct work_struct *work)
- {
- struct tavarua_device *radio;
- if (unlikely(work == NULL)) {
- FMDERR("%s:work is null", __func__);
- return;
- }
- radio = container_of(work, struct tavarua_device, work.work);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return;
- }
- FMDERR("%s: Releasing the FM I2S GPIO\n", __func__);
- if (radio->pdata->config_i2s_gpio != NULL)
- radio->pdata->config_i2s_gpio(FM_I2S_OFF);
- FMDERR("%s: Shutting down FM SOC\n", __func__);
- radio->pdata->fm_shutdown(radio->pdata);
- complete(&radio->shutdown_done);
- }
- /*************************************************************************
- * irq helper functions
- ************************************************************************/
- /*=============================================================================
- FUNCTION: tavarua_request_irq
- =============================================================================*/
- /**
- This function is called to acquire a FM GPIO and enable FM interrupts.
- @param radio: structure pointer passed by client.
- @return 0 if success else otherwise.
- */
- static int tavarua_request_irq(struct tavarua_device *radio)
- {
- int retval;
- int irq;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- irq = radio->pdata->irq;
- /* A workqueue created with create_workqueue() will have one worker thread
- * for each CPU on the system; create_singlethread_workqueue(), instead,
- * creates a workqueue with a single worker process. The name of the queue
- * is limited to ten characters; it is only used for generating the "command"
- * for the kernel thread(s) (which can be seen in ps or top).
- */
- /* allocate an interrupt line */
- /* On success, request_irq() returns 0 if everything goes as
- planned. Your interrupt handler will start receiving its
- interrupts immediately. On failure, request_irq()
- returns:
- -EINVAL
- The IRQ number you requested was either
- invalid or reserved, or your passed a NULL
- pointer for the handler() parameter.
- -EBUSY The IRQ you requested is already being
- handled, and the IRQ cannot be shared.
- -ENXIO The m68k returns this value for an invalid
- IRQ number.
- */
- /* Use request_any_context_irq, So that it might work for nested or
- nested interrupts. in MSM8x60, FM is connected to PMIC GPIO and it
- is a nested interrupt*/
- retval = request_any_context_irq(irq, tavarua_isr,
- IRQ_TYPE_EDGE_FALLING, "fm interrupt", radio);
- if (retval < 0) {
- FMDERR("Couldn't acquire FM gpio %d\n", irq);
- return retval;
- } else {
- FMDBG("FM GPIO %d registered\n", irq);
- }
- retval = enable_irq_wake(irq);
- if (retval < 0) {
- FMDERR("Could not enable FM interrupt\n ");
- free_irq(irq , radio);
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_disable_irq
- =============================================================================*/
- /**
- This function is called to disable FM irq and free up FM interrupt handling
- resources.
- @param radio: structure pointer passed by client.
- @return 0 if success else otherwise.
- */
- static int tavarua_disable_irq(struct tavarua_device *radio)
- {
- int irq;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- irq = radio->pdata->irq;
- disable_irq_wake(irq);
- free_irq(irq, radio);
- cancel_delayed_work_sync(&radio->work);
- flush_workqueue(radio->wqueue);
- return 0;
- }
- static int optimized_search_algorithm(struct tavarua_device *radio,
- int region)
- {
- unsigned char adie_type_bahma;
- int retval = 0;
- unsigned int rdsMask = 0;
- unsigned char value = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- adie_type_bahma = is_bahama();
- switch (region) {
- case TAVARUA_REGION_US:
- /*
- Radio band for all the 200KHz channel-spaced regions
- coming under EUROPE too, have been set as TAVARUA_REGION_US.
- */
- FMDBG("%s: The region selected from APP is"
- " : TAVARUA_REGION_US", __func__);
- break;
- case TAVARUA_REGION_EU:
- /*
- Radio band for all the 50KHz channel-spaced regions
- coming under EUROPE, have been set as TAVARUA_REGION_EU.
- */
- FMDBG("%s: The region selected from APP is : "
- "TAVARUA_REGION_EU", __func__);
- break;
- case TAVARUA_REGION_JAPAN:
- /*
- Radio band for the 100KHz channel-spaced JAPAN region
- has been set as TAVARUA_REGION_JAPAN.
- */
- FMDBG("%s: The region selected from APP is"
- " : TAVARUA_REGION_JAPAN", __func__);
- break;
- case TAVARUA_REGION_JAPAN_WIDE:
- /*
- Radio band for the 50KHz channel-spaced JAPAN WIDE region
- has been set as TAVARUA_REGION_JAPAN_WIDE.
- */
- FMDBG("%s: The region selected from APP is"
- " : TAVARUA_REGION_JAPAN_WIDE", __func__);
- break;
- case TAVARUA_REGION_OTHER:
- /*
- Radio band for all the 100KHz channel-spaced regions
- including those coming under EUROPE have been set as
- TAVARUA_REGION_OTHER.
- */
- FMDBG("%s: The region selected from APP is"
- " : TAVARUA_REGION_OTHER", __func__);
- break;
- default:
- pr_err("%s: Should not reach here.", __func__);
- break;
- }
- /* Enable or Disable the 200KHz enforcer */
- switch (region) {
- case TAVARUA_REGION_US:
- case TAVARUA_REGION_JAPAN:
- case TAVARUA_REGION_OTHER:
- /*
- These are the 3 bands for which we need to enable the
- 200KHz enforcer in ADVCTL reg.
- */
- if (adie_type_bahma) {
- FMDBG("Adie type : Bahama\n");
- FMDBG("%s: Enabling the 200KHz enforcer for"
- " Region : %d", __func__, region);
- /*Enable the 200KHz enforcer*/
- retval = tavarua_read_registers(radio,
- ADVCTRL, 1);
- if (retval >= 0) {
- rdsMask = radio->registers[ADVCTRL];
- SET_REG_FIELD(rdsMask, ENF_SRCH200khz,
- SRCH200KHZ_OFFSET, SRCH_MASK);
- retval = tavarua_write_register(radio,
- ADVCTRL, rdsMask);
- } else
- return retval;
- } /* if Marimba do nothing */
- break;
- case TAVARUA_REGION_EU:
- case TAVARUA_REGION_JAPAN_WIDE:
- /*
- These are the 2 bands for which we need to disable the
- 200KHz enforcer in ADVCTL reg.
- Radio band for all the 50KHz channel-spaced regions
- coming under EUROPE have been set as TAVARUA_REGION_EU.
- */
- if (adie_type_bahma) {
- FMDBG("Adie type : Bahama\n");
- FMDBG("%s: Disabling the 200KHz enforcer for"
- " Region : %d", __func__, region);
- /*
- Disable 200KHz enforcer for all 50 KHz
- spaced regions.
- */
- retval = tavarua_read_registers(radio,
- ADVCTRL, 1);
- if (retval >= 0) {
- rdsMask = radio->registers[ADVCTRL];
- SET_REG_FIELD(rdsMask, NO_SRCH200khz,
- SRCH200KHZ_OFFSET, SRCH_MASK);
- retval = tavarua_write_register(radio,
- ADVCTRL, rdsMask);
- } else
- return retval;
- } /* if Marimba do nothing */
- break;
- default:
- FMDBG("%s: Defaulting in case of Enabling/Disabling"
- "the 200KHz Enforcer", __func__);
- break;
- }
- /* Set channel spacing */
- switch (region) {
- case TAVARUA_REGION_US:
- if ((adie_type_bahma) && (bahama_version == 0x09)) {
- FMDBG("Adie type : Bahama\n");
- /*
- Configuring all 200KHZ spaced regions as 100KHz due to
- change in the new Bahma FM SoC search algorithm.
- */
- value = FM_CH_SPACE_100KHZ;
- } else if ((adie_type_bahma) && (bahama_version == 0x0a)) {
- FMDBG("Adie type : Bahama B1\n");
- value = FM_CH_SPACE_200KHZ;
- } else {
- FMDBG("Adie type : Marimba\n");
- value = FM_CH_SPACE_200KHZ;
- }
- break;
- case TAVARUA_REGION_JAPAN:
- case TAVARUA_REGION_OTHER:
- if ((adie_type_bahma) && (bahama_version == 0x09)) {
- FMDBG("Adie type : Bahama\n");
- FMDBG("%s: Configuring the channel-spacing as 50KHz"
- "for the Region : %d", __func__, region);
- /*
- Configuring all 100KHZ spaced regions as 50KHz due to
- change in the new Bahma FM SoC search algorithm.
- */
- value = FM_CH_SPACE_50KHZ;
- } else if ((adie_type_bahma) && (bahama_version == 0x0a)) {
- FMDBG("Adie type : Bahama B1\n");
- value = FM_CH_SPACE_100KHZ;
- } else {
- FMDBG("Adie type : Marimba\n");
- value = FM_CH_SPACE_100KHZ;
- }
- break;
- case TAVARUA_REGION_EU:
- case TAVARUA_REGION_JAPAN_WIDE:
- value = FM_CH_SPACE_50KHZ;
- break;
- default:
- FMDBG("%s: Defualting in case of Channel-Spacing", __func__);
- break;
- }
- SET_REG_FIELD(radio->registers[RDCTRL], value,
- RDCTRL_CHSPACE_OFFSET, RDCTRL_CHSPACE_MASK);
- return retval;
- }
- /*************************************************************************
- * fops/IOCTL helper functions
- ************************************************************************/
- /*=============================================================================
- FUNCTION: tavarua_search
- =============================================================================*/
- /**
- This interface sets the search control features.
- @param radio: structure pointer passed by client.
- @param on: The value of a control.
- @param dir: FM search direction.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_search(struct tavarua_device *radio, int on, int dir)
- {
- enum search_t srch;
- FMDBG("In tavarua_search\n");
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- srch = radio->registers[SRCHCTRL] & SRCH_MODE;
- if (on) {
- radio->registers[SRCHRDS1] = 0x00;
- radio->registers[SRCHRDS2] = 0x00;
- /* Set freq band */
- switch (srch) {
- case SCAN_FOR_STRONG:
- case SCAN_FOR_WEAK:
- radio->srch_params.get_list = 1;
- radio->registers[SRCHRDS2] =
- radio->srch_params.preset_num;
- break;
- case RDS_SEEK_PTY:
- case RDS_SCAN_PTY:
- radio->registers[SRCHRDS2] =
- radio->srch_params.srch_pty;
- break;
- case RDS_SEEK_PI:
- radio->registers[SRCHRDS1] =
- (radio->srch_params.srch_pi & 0xFF00) >> 8;
- radio->registers[SRCHRDS2] =
- (radio->srch_params.srch_pi & 0x00FF);
- break;
- default:
- break;
- }
- radio->registers[SRCHCTRL] |= SRCH_ON;
- } else {
- radio->registers[SRCHCTRL] &= ~SRCH_ON;
- radio->srch_params.get_list = 0;
- }
- radio->registers[SRCHCTRL] = (dir << 3) |
- (radio->registers[SRCHCTRL] & 0xF7);
- FMDBG("SRCHCTRL <%x>\n", radio->registers[SRCHCTRL]);
- FMDBG("Search Started\n");
- return tavarua_write_registers(radio, SRCHRDS1,
- &radio->registers[SRCHRDS1], 3);
- }
- /*=============================================================================
- FUNCTION: tavarua_set_region
- =============================================================================*/
- /**
- This interface configures the FM radio.
- @param radio: structure pointer passed by client.
- @param req_region: FM band types. These types defines the FM band minimum and
- maximum frequencies in the FM band.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_set_region(struct tavarua_device *radio,
- int req_region)
- {
- int retval = 0;
- unsigned int rdsMask = 0;
- unsigned char xfr_buf[XFR_REG_NUM];
- unsigned char value;
- unsigned int spacing = 0.100 * FREQ_MUL;
- unsigned int band_low, band_high;
- unsigned int low_band_limit = 76.0 * FREQ_MUL;
- enum tavarua_region_t region = req_region;
- unsigned char adie_type_bahma;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- adie_type_bahma = is_bahama();
- /* Set freq band */
- if (region == TAVARUA_REGION_JAPAN)
- SET_REG_FIELD(radio->registers[RDCTRL], 1,
- RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
- else
- SET_REG_FIELD(radio->registers[RDCTRL], 0,
- RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
- /* Set De-emphasis and soft band range*/
- SET_REG_FIELD(radio->registers[RDCTRL], radio->region_params.emphasis,
- RDCTRL_DEEMPHASIS_OFFSET, RDCTRL_DEEMPHASIS_MASK);
- /* set RDS standard */
- SET_REG_FIELD(radio->registers[RDSCTRL], radio->region_params.rds_std,
- RDSCTRL_STANDARD_OFFSET, RDSCTRL_STANDARD_MASK);
- FMDBG("RDSCTRLL %x\n", radio->registers[RDSCTRL]);
- retval = tavarua_write_register(radio, RDSCTRL,
- radio->registers[RDSCTRL]);
- if (retval < 0) {
- FMDERR("Failed to set RDS/RBDS standard\n");
- return retval;
- }
- /* Set the lower and upper band limits*/
- retval = sync_read_xfr(radio, RADIO_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("failed to get RADIO_CONFIG\n");
- return retval;
- }
- band_low = (radio->region_params.band_low -
- low_band_limit) / spacing;
- band_high = (radio->region_params.band_high -
- low_band_limit) / spacing;
- xfr_buf[0] = RSH_DATA(band_low, 8);
- xfr_buf[1] = GET_ABS_VAL(band_low);
- xfr_buf[2] = RSH_DATA(band_high, 8);
- xfr_buf[3] = GET_ABS_VAL(band_high);
- xfr_buf[4] = 0; /* Active LOW */
- retval = sync_write_xfr(radio, RADIO_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("Could not set regional settings\n");
- return retval;
- }
- radio->region_params.region = region;
- /* Check for the FM Algorithm used */
- if (radio->enable_optimized_srch_alg) {
- FMDBG("Optimized Srch Algorithm!!!");
- optimized_search_algorithm(radio, region);
- } else {
- FMDBG("Native Srch Algorithm!!!");
- /* Enable/Disable the 200KHz enforcer */
- switch (region) {
- case TAVARUA_REGION_US:
- if (adie_type_bahma) {
- FMDBG("Adie type : Bahama\n");
- /*Enable the 200KHz enforcer*/
- retval = tavarua_read_registers(radio,
- ADVCTRL, 1);
- if (retval >= 0) {
- rdsMask = radio->registers[ADVCTRL];
- SET_REG_FIELD(rdsMask, ENF_SRCH200khz,
- SRCH200KHZ_OFFSET, SRCH_MASK);
- retval = tavarua_write_register(radio,
- ADVCTRL, rdsMask);
- } else
- return retval;
- } /* if Marimba do nothing */
- break;
- case TAVARUA_REGION_EU:
- case TAVARUA_REGION_JAPAN:
- case TAVARUA_REGION_JAPAN_WIDE:
- default:
- if (adie_type_bahma) {
- FMDBG("Adie type : Bahama\n");
- /*
- Disable 200KHz enforcer for all 100/50 KHz
- spaced regions.
- */
- retval = tavarua_read_registers(radio,
- ADVCTRL, 1);
- if (retval >= 0) {
- rdsMask = radio->registers[ADVCTRL];
- SET_REG_FIELD(rdsMask, NO_SRCH200khz,
- SRCH200KHZ_OFFSET, SRCH_MASK);
- retval = tavarua_write_register(radio,
- ADVCTRL, rdsMask);
- } else
- return retval;
- } /* if Marimba do nothing */
- break;
- }
- /* Set channel spacing */
- if (region == TAVARUA_REGION_US) {
- if (adie_type_bahma) {
- FMDBG("Adie type : Bahama\n");
- /*
- Configuring all 200KHZ spaced regions as
- 100KHz due to change in the new Bahma
- FM SoC search algorithm.
- */
- value = FM_CH_SPACE_100KHZ;
- } else {
- FMDBG("Adie type : Marimba\n");
- value = FM_CH_SPACE_200KHZ;
- }
- } else {
- /*
- Set the channel spacing as configured from
- the upper layers.
- */
- value = radio->region_params.spacing;
- }
- SET_REG_FIELD(radio->registers[RDCTRL], value,
- RDCTRL_CHSPACE_OFFSET, RDCTRL_CHSPACE_MASK);
- }
- /* Write the config values into RDCTL register */
- FMDBG("RDCTRL: %x\n", radio->registers[RDCTRL]);
- retval = tavarua_write_register(radio, RDCTRL,
- radio->registers[RDCTRL]);
- if (retval < 0) {
- FMDERR("Could not set region in rdctrl\n");
- return retval;
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_get_freq
- =============================================================================*/
- /**
- This interface gets the current frequency.
- @param radio: structure pointer passed by client.
- @param freq: struct v4l2_frequency. This will be set to the resultant
- frequency in units of 62.5 kHz on success.
- NOTE:
- To get the current tuner or modulator radio frequency applications set the
- tuner field of a struct v4l2_frequency to the respective tuner or modulator
- number (only input devices have tuners, only output devices have modulators),
- zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
- pointer to this structure. The driver stores the current frequency in the
- frequency field.
- Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner or
- struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set, in
- units of 62.5 Hz.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_get_freq(struct tavarua_device *radio,
- struct v4l2_frequency *freq)
- {
- int retval;
- unsigned short chan;
- unsigned int band_bottom;
- unsigned int spacing;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(freq == NULL)) {
- FMDERR("%s:freq is null", __func__);
- return -EINVAL;
- }
- band_bottom = radio->region_params.band_low;
- spacing = 0.100 * FREQ_MUL;
- /* read channel */
- retval = tavarua_read_registers(radio, FREQ, 2);
- chan = radio->registers[FREQ];
- /* Frequency (MHz) = 100 (kHz) x Channel + Bottom of Band (MHz) */
- freq->frequency = spacing * chan + band_bottom;
- if (radio->registers[TUNECTRL] & ADD_OFFSET)
- freq->frequency += 800;
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_set_freq
- =============================================================================*/
- /**
- This interface sets the current frequency.
- @param radio: structure pointer passed by client.
- @param freq: desired frequency sent by the client in 62.5 kHz units.
- NOTE:
- To change the current tuner or modulator radio frequency, applications
- initialize the tuner, type and frequency fields, and the reserved array of a
- struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer to
- this structure. When the requested frequency is not possible the driver
- assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
- write-only ioctl, it does not return the actual new frequency.
- Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner
- or struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set,
- in units of 62.5 Hz.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_set_freq(struct tavarua_device *radio, unsigned int freq)
- {
- unsigned int band_bottom;
- unsigned char chan;
- unsigned char cmd[] = {0x00, 0x00};
- unsigned int spacing;
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- band_bottom = radio->region_params.band_low;
- spacing = 0.100 * FREQ_MUL;
- if ((freq % 1600) == 800) {
- cmd[1] = ADD_OFFSET;
- freq -= 800;
- }
- /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / 100 (kHz) */
- chan = (freq - band_bottom) / spacing;
- cmd[0] = chan;
- cmd[1] |= TUNE_STATION;
- radio->tune_req = 1;
- retval = tavarua_write_registers(radio, FREQ, cmd, 2);
- if (retval < 0)
- radio->tune_req = 0;
- return retval;
- }
- /**************************************************************************
- * File Operations Interface
- *************************************************************************/
- /*=============================================================================
- FUNCTION: tavarua_fops_read
- =============================================================================*/
- /**
- This function is called when a process, which already opened the dev file,
- attempts to read from it.
- In case of tavarua driver, it is called to read RDS data.
- @param file: file descriptor.
- @param buf: The buffer to fill with data.
- @param count: The length of the buffer in bytes.
- @param ppos: Our offset in the file.
- @return The number of bytes put into the buffer on sucess.
- -EFAULT if there is no access to user buffer
- */
- static ssize_t tavarua_fops_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- struct kfifo *rds_buf;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(buf == NULL)) {
- FMDERR("%s:buf is null", __func__);
- return -EINVAL;
- }
- rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
- /* block if no new data available */
- while (!kfifo_len(rds_buf)) {
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- if (wait_event_interruptible(radio->read_queue,
- kfifo_len(rds_buf)) < 0)
- return -EINTR;
- }
- /* calculate block count from byte count */
- count /= BYTES_PER_BLOCK;
- /* check if we can write to the user buffer */
- if (!access_ok(VERIFY_WRITE, buf, count*BYTES_PER_BLOCK))
- return -EFAULT;
- /* copy RDS block out of internal buffer and to user buffer */
- return kfifo_out_locked(rds_buf, buf, count*BYTES_PER_BLOCK,
- &radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
- }
- /*=============================================================================
- FUNCTION: tavarua_fops_write
- =============================================================================*/
- /**
- This function is called when a process, which already opened the dev file,
- attempts to write to it.
- In case of tavarua driver, it is called to write RDS data to host.
- @param file: file descriptor.
- @param buf: The buffer which has data to write.
- @param count: The length of the buffer.
- @param ppos: Our offset in the file.
- @return The number of bytes written from the buffer.
- */
- static ssize_t tavarua_fops_write(struct file *file, const char __user *data,
- size_t count, loff_t *ppos)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval = 0;
- int bytes_to_copy;
- int bytes_copied = 0;
- int bytes_left;
- int chunk_index = 0;
- unsigned char tx_data[XFR_REG_NUM];
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(data == NULL)) {
- FMDERR("%s:data is null", __func__);
- return -EINVAL;
- }
- /* Disable TX of this type first */
- switch (radio->tx_mode) {
- case TAVARUA_TX_RT:
- bytes_left = min((int)count, MAX_RT_LENGTH);
- tx_data[1] = 0;
- break;
- case TAVARUA_TX_PS:
- bytes_left = min((int)count, MAX_PS_LENGTH);
- tx_data[4] = 0;
- break;
- default:
- FMDERR("%s: Unknown TX mode\n", __func__);
- return -1;
- }
- retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
- if (retval < 0)
- return retval;
- /* send payload to FM hardware */
- while (bytes_left) {
- chunk_index++;
- bytes_to_copy = min(bytes_left, XFR_REG_NUM);
- if (copy_from_user(tx_data, data + bytes_copied, bytes_to_copy))
- return -EFAULT;
- retval = sync_write_xfr(radio, radio->tx_mode +
- chunk_index, tx_data);
- if (retval < 0)
- return retval;
- bytes_copied += bytes_to_copy;
- bytes_left -= bytes_to_copy;
- }
- /* send the header */
- switch (radio->tx_mode) {
- case TAVARUA_TX_RT:
- FMDBG("Writing RT header\n");
- tx_data[0] = bytes_copied;
- tx_data[1] = TX_ON | 0x03; /* on | PTY */
- tx_data[2] = 0x12; /* PI high */
- tx_data[3] = 0x34; /* PI low */
- break;
- case TAVARUA_TX_PS:
- FMDBG("Writing PS header\n");
- tx_data[0] = chunk_index;
- tx_data[1] = 0x03; /* PTY */
- tx_data[2] = 0x12; /* PI high */
- tx_data[3] = 0x34; /* PI low */
- tx_data[4] = TX_ON | 0x01;
- break;
- default:
- FMDERR("%s: Unknown TX mode\n", __func__);
- return -1;
- }
- retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
- if (retval < 0)
- return retval;
- FMDBG("done writing: %d\n", retval);
- return bytes_copied;
- }
- /*=============================================================================
- FUNCTION: tavarua_fops_open
- =============================================================================*/
- /**
- This function is called when a process tries to open the device file, like
- "cat /dev/mycharfile"
- @param file: file descriptor.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_fops_open(struct file *file)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- struct marimba config = { .mod_id = SLAVE_ID_BAHAMA};
- int retval = -ENODEV;
- unsigned char value;
- /* FM core bring up */
- int i = 0;
- char fm_ctl0_part1[] = { 0xCA, 0xCE, 0xD6 };
- char fm_ctl1[] = { 0x03 };
- char fm_ctl0_part2[] = { 0xB6, 0xB7 };
- char buffer[] = {0x00, 0x48, 0x8A, 0x8E, 0x97, 0xB7};
- int bahama_present = -ENODEV;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- INIT_DELAYED_WORK(&radio->work, read_int_stat);
- if (!atomic_dec_and_test(&radio->users)) {
- pr_err("%s: Device already in use."
- "Try again later", __func__);
- atomic_inc(&radio->users);
- return -EBUSY;
- }
- /* initial gpio pin config & Power up */
- retval = radio->pdata->fm_setup(radio->pdata);
- if (retval) {
- printk(KERN_ERR "%s: failed config gpio & pmic\n", __func__);
- goto open_err_setup;
- }
- if (radio->pdata->config_i2s_gpio != NULL) {
- retval = radio->pdata->config_i2s_gpio(FM_I2S_ON);
- if (retval) {
- printk(KERN_ERR "%s: failed config gpio\n", __func__);
- goto config_i2s_err;
- }
- }
- /* enable irq */
- retval = tavarua_request_irq(radio);
- if (retval < 0) {
- printk(KERN_ERR "%s: failed to request irq\n", __func__);
- goto open_err_req_irq;
- }
- /* call top level marimba interface here to enable FM core */
- FMDBG("initializing SoC\n");
- bahama_present = is_bahama();
- if (bahama_present == -ENODEV)
- return -ENODEV;
- marimba_set_fm_status(radio->marimba, true);
- if (bahama_present)
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- else
- radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
- value = FM_ENABLE;
- retval = marimba_write_bit_mask(&config,
- MARIMBA_XO_BUFF_CNTRL, &value, 1, value);
- if (retval < 0) {
- printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n",
- __func__);
- goto open_err_all;
- }
- /* Bring up FM core */
- if (bahama_present) {
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- /* Read the Bahama version*/
- retval = marimba_read_bit_mask(&config,
- 0x00, &bahama_version, 1, 0x1F);
- if (retval < 0) {
- printk(KERN_ERR "%s: version read failed",
- __func__);
- goto open_err_all;
- }
- /* Check for Bahama V2 variant*/
- if ((bahama_version == 0x09) || (bahama_version == 0x0a)) {
- /* In case of Bahama v2, forcefully enable the
- * internal analog and digital voltage controllers
- */
- value = 0x06;
- /* value itself used as mask in these writes*/
- retval = marimba_write_bit_mask(&config,
- BAHAMA_LDO_DREG_CTL0, &value, 1, value);
- if (retval < 0) {
- printk(KERN_ERR "%s:0xF0 write failed\n",
- __func__);
- goto open_err_all;
- }
- value = 0x86;
- retval = marimba_write_bit_mask(&config,
- BAHAMA_LDO_AREG_CTL0, &value, 1, value);
- if (retval < 0) {
- printk(KERN_ERR "%s:0xF4 write failed\n",
- __func__);
- goto open_err_all;
- }
- }
- /*write FM mode*/
- retval = tavarua_write_register(radio, BAHAMA_FM_MODE_REG,
- BAHAMA_FM_MODE_NORMAL);
- if (retval < 0) {
- printk(KERN_ERR "failed to set the FM mode: %d\n",
- retval);
- goto open_err_all;
- }
- /*Write first sequence of bytes to FM_CTL0*/
- for (i = 0; i < 3; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL0:set-1 failure: %d\n",
- retval);
- goto open_err_all;
- }
- }
- /*Write the FM_CTL1 sequence*/
- for (i = 0; i < 1; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL1 write failure: %d\n",
- retval);
- goto open_err_all;
- }
- }
- /*Write second sequence of bytes to FM_CTL0*/
- for (i = 0; i < 2; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL0:set-2 failure: %d\n",
- retval);
- goto open_err_all;
- }
- }
- } else {
- retval = tavarua_write_registers(radio, LEAKAGE_CNTRL,
- buffer, 6);
- if (retval < 0) {
- printk(KERN_ERR "%s: failed to bring up FM Core\n",
- __func__);
- goto open_err_all;
- }
- }
- /* Wait for interrupt i.e. complete(&radio->sync_req_done); call */
- /*Initialize the completion variable for
- for the proper behavior*/
- init_completion(&radio->sync_req_done);
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout))) {
- retval = -1;
- FMDERR("Timeout waiting for initialization\n");
- }
- /* get Chip ID */
- retval = tavarua_write_register(radio, XFRCTRL, CHIPID);
- if (retval < 0)
- goto open_err_all;
- msleep(TAVARUA_DELAY);
- tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
- if (radio->registers[XFRCTRL] != CHIPID)
- goto open_err_all;
- radio->chipID = (radio->registers[XFRCTRL+2] << 24) |
- (radio->registers[XFRCTRL+5] << 16) |
- (radio->registers[XFRCTRL+6] << 8) |
- (radio->registers[XFRCTRL+7]);
- printk(KERN_WARNING DRIVER_NAME ": Chip ID %x\n", radio->chipID);
- if (radio->chipID == MARIMBA_A0) {
- printk(KERN_WARNING DRIVER_NAME ": Unsupported hardware: %x\n",
- radio->chipID);
- retval = -1;
- goto open_err_all;
- }
- radio->handle_irq = 0;
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- return 0;
- open_err_all:
- /*Disable FM in case of error*/
- value = 0x00;
- marimba_write_bit_mask(&config, MARIMBA_XO_BUFF_CNTRL,
- &value, 1, value);
- tavarua_disable_irq(radio);
- open_err_req_irq:
- if (radio->pdata->config_i2s_gpio != NULL)
- radio->pdata->config_i2s_gpio(FM_I2S_OFF);
- config_i2s_err:
- radio->pdata->fm_shutdown(radio->pdata);
- open_err_setup:
- marimba_set_fm_status(radio->marimba, false);
- radio->handle_irq = 1;
- atomic_inc(&radio->users);
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_fops_release
- =============================================================================*/
- /**
- This function is called when a process closes the device file.
- @param file: file descriptor.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_fops_release(struct file *file)
- {
- int retval;
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- struct marimba config = { .mod_id = SLAVE_ID_BAHAMA};
- unsigned char value;
- int i = 0;
- /*FM Core shutdown sequence for Bahama*/
- char fm_ctl0_part1[] = { 0xB7 };
- char fm_ctl1[] = { 0x03 };
- char fm_ctl0_part2[] = { 0x9F, 0x48, 0x02 };
- int bahama_present = -ENODEV;
- /*FM Core shutdown sequence for Marimba*/
- char buffer[] = {0x18, 0xB7, 0x48};
- bool bt_status = false;
- int index;
- /* internal regulator controllers DREG_CTL0, AREG_CTL0
- * has to be kept in the valid state based on the bt status.
- * 1st row is the state when no clients are active,
- * and the second when bt is in on state.
- */
- char internal_vreg_ctl[2][2] = {
- { 0x04, 0x84 },
- { 0x00, 0x80 }
- };
- if (!radio) {
- pr_err("%s: Radio device not available...", __func__);
- return -ENODEV;
- }
- FMDBG("In %s", __func__);
- FMDBG("%s, Disabling the IRQs\n", __func__);
- /* disable irq */
- retval = tavarua_disable_irq(radio);
- if (retval < 0) {
- printk(KERN_ERR "%s: failed to disable irq\n", __func__);
- return retval;
- }
- /* disable radio ctrl */
- retval = tavarua_write_register(radio, RDCTRL, 0x00);
- if (retval < 0) {
- printk(KERN_ERR "%s: failed to disable FM\n", __func__);
- return retval;
- }
- init_completion(&radio->shutdown_done);
- bahama_present = is_bahama();
- if (bahama_present == -ENODEV)
- return -ENODEV;
- INIT_DELAYED_WORK(&radio->work, fm_shutdown);
- if (bahama_present) {
- /*Write first sequence of bytes to FM_CTL0*/
- for (i = 0; i < 1; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL0:Set-1 failure: %d\n",
- retval);
- break;
- }
- }
- /*Write the FM_CTL1 sequence*/
- for (i = 0; i < 1; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL1 failure: %d\n",
- retval);
- break;
- }
- }
- /*Write second sequence of bytes to FM_CTL0*/
- for (i = 0; i < 3; i++) {
- retval = tavarua_write_register(radio,
- BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
- if (retval < 0) {
- printk(KERN_ERR "FM_CTL0:Set-2 failure: %d\n",
- retval);
- break;
- }
- }
- } else {
- retval = tavarua_write_registers(radio, FM_CTL0,
- buffer, sizeof(buffer)/sizeof(buffer[0]));
- if (retval < 0) {
- printk(KERN_ERR "%s: failed to bring down the FM Core\n",
- __func__);
- return retval;
- }
- }
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- bt_status = marimba_get_bt_status(radio->marimba);
- /* Set the index based on the bt status*/
- index = bt_status ? 1 : 0;
- /* Check for Bahama's existance and Bahama V2 variant*/
- if (bahama_present
- && (bahama_version == 0x09 || bahama_version == 0x0a)) {
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- /* actual value itself used as mask*/
- retval = marimba_write_bit_mask(&config,
- BAHAMA_LDO_DREG_CTL0, &internal_vreg_ctl[bt_status][0],
- 1, internal_vreg_ctl[index][0]);
- if (retval < 0) {
- printk(KERN_ERR "%s:0xF0 write failed\n", __func__);
- goto exit;
- }
- /* actual value itself used as mask*/
- retval = marimba_write_bit_mask(&config,
- BAHAMA_LDO_AREG_CTL0, &internal_vreg_ctl[bt_status][1],
- 1, internal_vreg_ctl[index][1]);
- if (retval < 0) {
- printk(KERN_ERR "%s:0xF4 write failed\n", __func__);
- goto exit;
- }
- } else {
- /* disable fm core */
- radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
- }
- value = 0x00;
- retval = marimba_write_bit_mask(&config, MARIMBA_XO_BUFF_CNTRL,
- &value, 1, FM_ENABLE);
- if (retval < 0) {
- printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n", __func__);
- goto exit;
- }
- exit:
- FMDBG("%s, Calling fm_shutdown\n", __func__);
- queue_delayed_work(radio->wqueue, &radio->work,
- msecs_to_jiffies(TAVARUA_DELAY/2));
- /* teardown gpio and pmic */
- marimba_set_fm_status(radio->marimba, false);
- wait_for_completion(&radio->shutdown_done);
- radio->handle_irq = 1;
- radio->lp_mode = 1;
- radio->spur_table_size = 0;
- atomic_inc(&radio->users);
- radio->marimba->mod_id = SLAVE_ID_BAHAMA;
- flush_workqueue(radio->wqueue);
- return retval;
- }
- /*
- * tavarua_fops - file operations interface
- */
- static const struct v4l2_file_operations tavarua_fops = {
- .owner = THIS_MODULE,
- .read = tavarua_fops_read,
- .write = tavarua_fops_write,
- .ioctl = video_ioctl2,
- .open = tavarua_fops_open,
- .release = tavarua_fops_release,
- };
- /*************************************************************************
- * Video4Linux Interface
- *************************************************************************/
- /*
- * tavarua_v4l2_queryctrl - query control
- */
- static struct v4l2_queryctrl tavarua_v4l2_queryctrl[] = {
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 15,
- },
- {
- .id = V4L2_CID_AUDIO_BALANCE,
- .flags = V4L2_CTRL_FLAG_DISABLED,
- },
- {
- .id = V4L2_CID_AUDIO_BASS,
- .flags = V4L2_CTRL_FLAG_DISABLED,
- },
- {
- .id = V4L2_CID_AUDIO_TREBLE,
- .flags = V4L2_CTRL_FLAG_DISABLED,
- },
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_AUDIO_LOUDNESS,
- .flags = V4L2_CTRL_FLAG_DISABLED,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SRCHMODE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Search mode",
- .minimum = 0,
- .maximum = 7,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Search dwell time",
- .minimum = 0,
- .maximum = 7,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SRCHON,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Search on/off",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_STATE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "radio 0ff/rx/tx/reset",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_REGION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "radio standard",
- .minimum = 0,
- .maximum = 2,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Signal Threshold",
- .minimum = 0x80,
- .maximum = 0x7F,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Search PTY",
- .minimum = 0,
- .maximum = 31,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Search PI",
- .minimum = 0,
- .maximum = 0xFF,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Preset num",
- .minimum = 0,
- .maximum = 12,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Emphasis",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "RDS standard",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_SPACING,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Channel spacing",
- .minimum = 0,
- .maximum = 2,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_RDSON,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "RDS on/off",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "RDS group mask",
- .minimum = 0,
- .maximum = 0xFFFFFFFF,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "RDS processing",
- .minimum = 0,
- .maximum = 0xFF,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "RDS data groups to buffer",
- .minimum = 1,
- .maximum = 21,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_PSALL,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "pass all ps strings",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Low power mode",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "headset/internal",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- },
- /* Private controls for FM TX*/
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Set PS REPEATCOUNT",
- .minimum = 0,
- .maximum = 15,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Stop PS NAME",
- .minimum = 0,
- .maximum = 1,
- },
- {
- .id = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Stop RT",
- .minimum = 0,
- .maximum = 1,
- },
- { .id = V4L2_CID_PRIVATE_SET_NOTCH_FILTER,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Notch filter",
- .minimum = 0,
- .maximum = 2,
- },
- };
- /*=============================================================================
- FUNCTION: tavarua_vidioc_querycap
- =============================================================================*/
- /**
- This function is called to query device capabilities.
- NOTE:
- All V4L2 devices support the VIDIOC_QUERYCAP ioctl. It is used to identify
- kernel devices compatible with this specification and to obtain information
- about driver and hardware capabilities. The ioctl takes a pointer to a struct
- v4l2_capability which is filled by the driver. When the driver is not
- compatible with this specification the ioctl returns an EINVAL error code.
- @param file: File descriptor returned by open().
- @param capability: pointer to struct v4l2_capability.
- @return On success 0 is returned, else error code.
- @return EINVAL: The device is not compatible with this specification.
- */
- static int tavarua_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(capability == NULL)) {
- FMDERR("%s:capability is null", __func__);
- return -EINVAL;
- }
- strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
- strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
- sprintf(capability->bus_info, "I2C");
- capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- capability->version = radio->chipID;
- return 0;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_queryctrl
- =============================================================================*/
- /**
- This function is called to query the device and driver for supported video
- controls (enumerate control items).
- NOTE:
- To query the attributes of a control, the applications set the id field of
- a struct v4l2_queryctrl and call the VIDIOC_QUERYCTRL ioctl with a pointer
- to this structure. The driver fills the rest of the structure or returns an
- EINVAL error code when the id is invalid.
- @param file: File descriptor returned by open().
- @param qc: pointer to struct v4l2_queryctrl.
- @return On success 0 is returned, else error code.
- @return EINVAL: The struct v4l2_queryctrl id is invalid.
- */
- static int tavarua_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
- {
- unsigned char i;
- int retval = -EINVAL;
- if (unlikely(qc == NULL)) {
- FMDERR("%s:qc is null", __func__);
- return -EINVAL;
- }
- for (i = 0; i < ARRAY_SIZE(tavarua_v4l2_queryctrl); i++) {
- if (qc->id && qc->id == tavarua_v4l2_queryctrl[i].id) {
- memcpy(qc, &(tavarua_v4l2_queryctrl[i]), sizeof(*qc));
- retval = 0;
- break;
- }
- }
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": query conv4ltrol failed with %d\n", retval);
- return retval;
- }
- static int update_spur_table(struct tavarua_device *radio)
- {
- unsigned char xfr_buf[XFR_REG_NUM];
- unsigned char size = 0, tbl_size = 0;
- int index = 0, offset = 0, addr = 0x0, val = 0;
- int retval = 0, temp = 0, cnt = 0, j = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- memset(xfr_buf, 0x0, XFR_REG_NUM);
- /* Read the SPUR Table Size */
- retval = xfr_rdwr_data(radio, XFR_READ, 1, SPUR_TABLE_ADDR, &tbl_size);
- if (retval < 0) {
- FMDERR("%s: Failed to read SPUR table size\n", __func__);
- return retval;
- }
- /* Calculate the new SPUR Register address */
- val = addr = (SPUR_TABLE_START_ADDR + (tbl_size * 3));
- /* Save the SPUR Table length configured by user*/
- temp = radio->spur_table_size;
- /* COnfigure the new spur table length */
- size = (radio->spur_table_size + tbl_size);
- retval = xfr_rdwr_data(radio, XFR_WRITE, 1, SPUR_TABLE_ADDR, &size);
- if (retval < 0) {
- FMDERR("%s: Failed to configure SPUR table size\n", __func__);
- return retval;
- }
- /* Program the spur table entries */
- for (cnt = 0; cnt < (temp / 4); cnt++) {
- offset = 0;
- for (j = 0; j < 4; j++) {
- xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
- radio->spur_data.freq[index]), 1);
- xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
- radio->spur_data.freq[index]), 0);
- xfr_buf[offset++] =
- radio->spur_data.rmssi[index];
- index++;
- }
- retval = xfr_rdwr_data(radio, XFR_WRITE, (SPUR_DATA_SIZE * 4),
- addr, xfr_buf);
- if (retval < 0) {
- FMDERR("%s: Failed to program SPUR frequencies\n",
- __func__);
- return retval;
- }
- addr += (SPUR_DATA_SIZE * 4);
- }
- /* Program the additional SPUR Frequencies */
- temp = radio->spur_table_size;
- temp = (temp % 4);
- if (temp > 0) {
- offset = 0;
- for (j = 0; j < temp; j++) {
- xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
- radio->spur_data.freq[index]), 1);
- xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
- radio->spur_data.freq[index]), 0);
- xfr_buf[offset++] =
- radio->spur_data.rmssi[index];
- index++;
- }
- size = (temp * SPUR_DATA_SIZE);
- retval = xfr_rdwr_data(radio, XFR_WRITE, size, addr, xfr_buf);
- if (retval < 0) {
- FMDERR("%s: Failed to program SPUR frequencies\n",
- __func__);
- return retval;
- }
- }
- return retval;
- }
- static int xfr_rdwr_data(struct tavarua_device *radio, int op, int size,
- unsigned long offset, unsigned char *buf) {
- unsigned char xfr_buf[XFR_REG_NUM + 1];
- int retval = 0, temp = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(buf == NULL)) {
- FMDERR("%s:buf is null", __func__);
- return -EINVAL;
- }
- /* zero initialize the buffer */
- memset(xfr_buf, 0x0, XFR_REG_NUM);
- /* save the 'size' parameter */
- temp = size;
- /* Populate the XFR bytes */
- xfr_buf[XFR_MODE_OFFSET] = LSH_DATA(size, 1);
- xfr_buf[XFR_ADDR_MSB_OFFSET] = GET_FREQ(offset, 1);
- xfr_buf[XFR_ADDR_LSB_OFFSET] = GET_FREQ(offset, 0);
- if (op == XFR_READ) {
- if (size > XFR_REG_NUM) {
- FMDERR("%s: Cant read more than 16 bytes\n", __func__);
- return -EINVAL;
- }
- xfr_buf[XFR_MODE_OFFSET] |= (XFR_PEEK_MODE);
- size = 3;
- } else if (op == XFR_WRITE) {
- if (size > (XFR_REG_NUM - 2)) {
- FMDERR("%s: Cant write more than 14 bytes\n", __func__);
- return -EINVAL;
- }
- xfr_buf[XFR_MODE_OFFSET] |= (XFR_POKE_MODE);
- memcpy(&xfr_buf[XFR_DATA_OFFSET], buf, size);
- size += 3;
- }
- /* Perform the XFR READ/WRITE operation */
- init_completion(&radio->sync_req_done);
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, size);
- if (retval < 0) {
- FMDERR("%s: Failed to perform XFR operation\n", __func__);
- return retval;
- }
- /*Wait for the XFR interrupt */
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(WAIT_TIMEOUT))) {
- FMDERR("Timeout: No XFR interrupt");
- return -ETIMEDOUT;
- }
- /*
- * For XFR READ operation save the XFR data provided by the SOC.
- * Firmware reads the data from the address specified and places
- * them in to the registers XFRDAT0-XFRDAT15 which the host can read.
- */
- size = temp;
- if (op == XFR_READ) {
- retval = tavarua_read_registers(radio, XFRDAT0, size);
- if (retval < 0) {
- FMDERR("%s: Failed to read the XFR data\n", __func__);
- return retval;
- }
- if (buf != NULL)
- memcpy(buf, &radio->registers[XFRDAT0], size);
- else {
- FMDERR("%s: No buffer to copy XFR data\n", __func__);
- return -EINVAL;
- }
- }
- return retval;
- }
- static int peek_MPX_DCC(struct tavarua_device *radio)
- {
- int retval = 0;
- unsigned char xfr_buf[XFR_REG_NUM];
- int MPX_DCC[] = { 0 };
- int DCC = 0;
- int ct = 0;
- unsigned char size = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- /*
- Poking the MPX_DCC_BYPASS register to freeze the
- value of MPX_DCC from changing while we access it
- */
- /*Poking the MPX_DCC_BYPASS register : 0x88C0 */
- size = 0x01;
- xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
- xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
- xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
- xfr_buf[3] = 0x01;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
- if (retval < 0) {
- FMDBG("Failed to write\n");
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*15);
- for (ct = 0; ct < 5; ct++)
- xfr_buf[ct] = 0;
- /* Peeking Regs 0x88C2-0x88C4 */
- size = 0x03;
- xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
- xfr_buf[1] = MPX_DCC_PEEK_MSB_REG1;
- xfr_buf[2] = MPX_DCC_PEEK_LSB_REG1;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
- if (retval < 0) {
- FMDBG("Failed to write\n");
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- retval = tavarua_read_registers(radio, XFRDAT0, 3);
- if (retval < 0) {
- printk(KERN_INFO "INT_DET: Read failure\n");
- return retval;
- }
- MPX_DCC[0] = (int)radio->registers[XFRDAT0];
- MPX_DCC[1] = (int)radio->registers[XFRDAT1];
- MPX_DCC[2] = (int)radio->registers[XFRDAT2];
- /*
- Form the final MPX_DCC parameter
- MPX_DCC[0] will form the LSB part
- MPX_DCC[1] will be the middle part and 4 bits of
- MPX_DCC[2] will be the MSB par of the 20-bit signed MPX_DCC
- */
- DCC = ((int)MPX_DCC[2] << 16) | ((int)MPX_DCC[1] << 8) |
- ((int)MPX_DCC[0]);
- /*
- if bit-19 is '1',set remaining bits to '1' & make it -tive
- */
- if (DCC & 0x00080000) {
- FMDBG(KERN_INFO "bit-19 is '1'\n");
- DCC |= 0xFFF00000;
- }
- /*
- Poking the MPX_DCC_BYPASS register to be back to normal
- */
- /*Poking the MPX_DCC_BYPASS register : 0x88C0 */
- size = 0x01;
- xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
- xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
- xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
- xfr_buf[3] = 0x00;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
- if (retval < 0) {
- FMDBG("Failed to write\n");
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- return DCC;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_g_ctrl
- =============================================================================*/
- /**
- This function is called to get the value of a control.
- NOTE:
- To get the current value of a control, applications initialize the id field
- of a struct v4l2_control and call the VIDIOC_G_CTRL ioctl with a pointer to
- this structure.
- When the id is invalid drivers return an EINVAL error code. When the value is
- out of bounds drivers can choose to take the closest valid value or return an
- ERANGE error code, whatever seems more appropriate.
- @param file: File descriptor returned by open().
- @param ctrl: pointer to struct v4l2_control.
- @return On success 0 is returned, else error code.
- @return EINVAL: The struct v4l2_control id is invalid.
- @return ERANGE: The struct v4l2_control value is out of bounds.
- @return EBUSY: The control is temporarily not changeable, possibly because
- another applications took over control of the device function this control
- belongs to.
- */
- static int tavarua_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval = 0;
- int cnt = 0;
- unsigned char xfr_buf[XFR_REG_NUM];
- signed char cRmssiThreshold;
- signed char ioc;
- unsigned char size = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(ctrl == NULL)) {
- FMDERR("%s:ctrl is null", __func__);
- return -EINVAL;
- }
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = radio->registers[IOCTRL] & 0x03 ;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
- ctrl->value = radio->registers[SRCHCTRL] & SRCH_MODE;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
- ctrl->value = (radio->registers[SRCHCTRL] & SCAN_DWELL) >> 4;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
- ctrl->value = (radio->registers[SRCHCTRL] & SRCH_ON) >> 7 ;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_STATE:
- ctrl->value = (radio->registers[RDCTRL] & 0x03);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_IOVERC:
- retval = tavarua_read_registers(radio, IOVERC, 1);
- if (retval < 0)
- return retval;
- ioc = radio->registers[IOVERC];
- ctrl->value = ioc;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_INTDET:
- size = 0x1;
- xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
- xfr_buf[1] = INTDET_PEEK_MSB;
- xfr_buf[2] = INTDET_PEEK_LSB;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
- if (retval < 0) {
- FMDBG("Failed to write\n");
- return retval;
- }
- FMDBG("INT_DET:Sync write success\n");
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- /* Read the XFRDAT0 register populated by FM SoC */
- retval = tavarua_read_registers(radio, XFRDAT0, 3);
- if (retval < 0) {
- FMDBG("INT_DET: Read failure\n");
- return retval;
- }
- ctrl->value = radio->registers[XFRDAT0];
- break;
- case V4L2_CID_PRIVATE_TAVARUA_MPX_DCC:
- ctrl->value = peek_MPX_DCC(radio);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_REGION:
- ctrl->value = radio->region_params.region;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
- retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDBG("[G IOCTL=V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
- FMDBG("sync_read_xfr error: [retval=%d]\n", retval);
- break;
- }
- /* Since RMSSI Threshold is signed value */
- cRmssiThreshold = (signed char)xfr_buf[0];
- ctrl->value = cRmssiThreshold;
- FMDBG("cRmssiThreshold: %d\n", cRmssiThreshold);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
- ctrl->value = radio->srch_params.srch_pty;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
- ctrl->value = radio->srch_params.srch_pi;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
- ctrl->value = radio->srch_params.preset_num;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
- ctrl->value = radio->region_params.emphasis;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
- ctrl->value = radio->region_params.rds_std;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SPACING:
- ctrl->value = radio->region_params.spacing;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSON:
- ctrl->value = radio->registers[RDSCTRL] & RDS_ON;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- if (retval > -1)
- ctrl->value = (xfr_buf[8] << 24) |
- (xfr_buf[9] << 16) |
- (xfr_buf[10] << 8) |
- xfr_buf[11];
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
- retval = tavarua_read_registers(radio, ADVCTRL, 1);
- if (retval > -1)
- ctrl->value = radio->registers[ADVCTRL];
- msleep(TAVARUA_DELAY*5);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA:
- retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
- FMDERR("sync_read_xfr [retval=%d]\n", retval);
- break;
- }
- ctrl->value = (unsigned char)xfr_buf[4];
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- if (retval > -1)
- ctrl->value = xfr_buf[1];
- break;
- case V4L2_CID_PRIVATE_TAVARUA_PSALL:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- if (retval > -1)
- ctrl->value = xfr_buf[12] & RDS_CONFIG_PSALL;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
- ctrl->value = radio->lp_mode;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
- ctrl->value = GET_REG_FIELD(radio->registers[IOCTRL],
- IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
- break;
- case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
- size = 0x04;
- xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
- xfr_buf[1] = ON_CHANNEL_TH_MSB;
- xfr_buf[2] = ON_CHANNEL_TH_LSB;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
- if (retval < 0) {
- pr_err("%s: Failed to write\n", __func__);
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- retval = tavarua_read_registers(radio, XFRDAT0, 4);
- if (retval < 0) {
- pr_err("%s: On Ch. DET: Read failure\n", __func__);
- return retval;
- }
- for (cnt = 0; cnt < 4; cnt++)
- FMDBG("On-Channel data set is : 0x%x\t",
- (int)radio->registers[XFRDAT0+cnt]);
- ctrl->value = LSH_DATA(radio->registers[XFRDAT0], 24) |
- LSH_DATA(radio->registers[XFRDAT0+1], 16) |
- LSH_DATA(radio->registers[XFRDAT0+2], 8) |
- (radio->registers[XFRDAT0+3]);
- FMDBG("The On Channel Threshold value is : 0x%x", ctrl->value);
- break;
- case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
- size = 0x04;
- xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
- xfr_buf[1] = OFF_CHANNEL_TH_MSB;
- xfr_buf[2] = OFF_CHANNEL_TH_LSB;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
- if (retval < 0) {
- pr_err("%s: Failed to write\n", __func__);
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- retval = tavarua_read_registers(radio, XFRDAT0, 4);
- if (retval < 0) {
- pr_err("%s: Off Ch. DET: Read failure\n", __func__);
- return retval;
- }
- for (cnt = 0; cnt < 4; cnt++)
- FMDBG("Off-channel data set is : 0x%x\t",
- (int)radio->registers[XFRDAT0+cnt]);
- ctrl->value = LSH_DATA(radio->registers[XFRDAT0], 24) |
- LSH_DATA(radio->registers[XFRDAT0+1], 16) |
- LSH_DATA(radio->registers[XFRDAT0+2], 8) |
- (radio->registers[XFRDAT0+3]);
- FMDBG("The Off Channel Threshold value is : 0x%x", ctrl->value);
- break;
- /*
- * These IOCTL's are place holders to keep the
- * driver compatible with change in frame works for IRIS
- */
- case V4L2_CID_PRIVATE_SINR_THRESHOLD:
- case V4L2_CID_PRIVATE_SINR_SAMPLES:
- case V4L2_CID_PRIVATE_IRIS_GET_SINR:
- retval = 0;
- break;
- case V4L2_CID_PRIVATE_VALID_CHANNEL:
- ctrl->value = radio->is_station_valid;
- break;
- default:
- retval = -EINVAL;
- }
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": get control failed with %d, id: %d\n", retval, ctrl->id);
- return retval;
- }
- static int tavarua_vidioc_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrl)
- {
- int retval = 0;
- int bytes_to_copy;
- int bytes_copied = 0;
- __u32 bytes_left = 0;
- int chunk_index = 0;
- char tx_data[XFR_REG_NUM];
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- char *data = NULL;
- int extra_name_byte = 0;
- int name_bytes = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(ctrl == NULL) ||
- unlikely(ctrl->controls == NULL) ||
- unlikely(ctrl->count <= 0)) {
- FMDERR("%s:ctrl is null", __func__);
- return -EINVAL;
- }
- switch ((ctrl->controls[0]).id) {
- case V4L2_CID_RDS_TX_PS_NAME: {
- FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
- /*Pass a sample PS string */
- chunk_index = 0;
- bytes_copied = 0;
- bytes_left = min((ctrl->controls[0]).size,
- (__u32)MAX_PS_LENGTH);
- data = (ctrl->controls[0]).string;
- /* send payload to FM hardware */
- while (bytes_left) {
- chunk_index++;
- FMDBG("chunk is %d", chunk_index);
- bytes_to_copy = min(bytes_left, (__u32)XFR_REG_NUM);
- /*Clear the tx_data */
- memset(tx_data, 0, XFR_REG_NUM);
- if (copy_from_user(tx_data,
- data + bytes_copied, bytes_to_copy))
- return -EFAULT;
- retval = sync_write_xfr(radio,
- RDS_PS_0 + chunk_index, tx_data);
- if (retval < 0) {
- FMDBG("sync_write_xfr: %d", retval);
- return retval;
- }
- bytes_copied += bytes_to_copy;
- bytes_left -= bytes_to_copy;
- }
- memset(tx_data, 0, XFR_REG_NUM);
- /*Write the PS Header*/
- FMDBG("Writing PS header\n");
- extra_name_byte = (bytes_copied%8) ? 1 : 0;
- name_bytes = (bytes_copied/8) + extra_name_byte;
- /*8 bytes are grouped as 1 name */
- tx_data[0] = (name_bytes) & MASK_TXREPCOUNT;
- tx_data[1] = radio->pty & MASK_PTY; /* PTY */
- tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
- tx_data[3] = radio->pi & MASK_PI_LSB;
- /* TX ctrl + repeatCount*/
- tx_data[4] = TX_ON |
- (radio->ps_repeatcount & MASK_TXREPCOUNT);
- retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
- if (retval < 0) {
- FMDBG("sync_write_xfr returned %d", retval);
- return retval;
- }
- } break;
- case V4L2_CID_RDS_TX_RADIO_TEXT: {
- chunk_index = 0;
- bytes_copied = 0;
- FMDBG("In V4L2_CID_RDS_TX_RADIO_TEXT\n");
- /*Pass a sample PS string */
- FMDBG("Passed RT String : %s\n",
- (ctrl->controls[0]).string);
- bytes_left =
- min((ctrl->controls[0]).size, (__u32)MAX_RT_LENGTH);
- data = (ctrl->controls[0]).string;
- /* send payload to FM hardware */
- while (bytes_left) {
- chunk_index++;
- bytes_to_copy = min(bytes_left, (__u32)XFR_REG_NUM);
- memset(tx_data, 0, XFR_REG_NUM);
- if (copy_from_user(tx_data,
- data + bytes_copied, bytes_to_copy))
- return -EFAULT;
- retval = sync_write_xfr(radio,
- RDS_RT_0 + chunk_index, tx_data);
- if (retval < 0)
- return retval;
- bytes_copied += bytes_to_copy;
- bytes_left -= bytes_to_copy;
- }
- /*Write the RT Header */
- tx_data[0] = bytes_copied;
- /* PTY */
- tx_data[1] = TX_ON | ((radio->pty & MASK_PTY) >> 8);
- /* PI high */
- tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
- /* PI low */
- tx_data[3] = radio->pi & MASK_PI_LSB;
- retval = sync_write_xfr(radio, RDS_RT_0 , tx_data);
- if (retval < 0)
- return retval;
- FMDBG("done RT writing: %d\n", retval);
- } break;
- default:
- {
- FMDBG("Shouldn't reach here\n");
- retval = -1;
- }
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_s_ctrl
- =============================================================================*/
- /**
- This function is called to set the value of a control.
- NOTE:
- To change the value of a control, applications initialize the id and value
- fields of a struct v4l2_control and call the VIDIOC_S_CTRL ioctl.
- When the id is invalid drivers return an EINVAL error code. When the value is
- out of bounds drivers can choose to take the closest valid value or return an
- ERANGE error code, whatever seems more appropriate.
- @param file: File descriptor returned by open().
- @param ctrl: pointer to struct v4l2_control.
- @return On success 0 is returned, else error code.
- @return EINVAL: The struct v4l2_control id is invalid.
- @return ERANGE: The struct v4l2_control value is out of bounds.
- @return EBUSY: The control is temporarily not changeable, possibly because
- another applications took over control of the device function this control
- belongs to.
- */
- static int tavarua_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval = 0, size = 0, cnt = 0;
- unsigned char value;
- unsigned char xfr_buf[XFR_REG_NUM];
- unsigned char tx_data[XFR_REG_NUM];
- unsigned char dis_buf[XFR_REG_NUM];
- unsigned int freq = 0, mpx_dcc = 0;
- unsigned long curr = 0, prev = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(ctrl == NULL)) {
- FMDERR("%s:ctrl is null", __func__);
- return -EINVAL;
- }
- memset(xfr_buf, 0x0, XFR_REG_NUM);
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- break;
- case V4L2_CID_AUDIO_MUTE:
- value = (radio->registers[IOCTRL] & ~IOC_HRD_MUTE) |
- (ctrl->value & 0x03);
- retval = tavarua_write_register(radio, IOCTRL, value);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
- value = (radio->registers[SRCHCTRL] & ~SRCH_MODE) |
- ctrl->value;
- radio->registers[SRCHCTRL] = value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
- value = (radio->registers[SRCHCTRL] & ~SCAN_DWELL) |
- (ctrl->value << 4);
- radio->registers[SRCHCTRL] = value;
- break;
- /* start/stop search */
- case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
- FMDBG("starting search\n");
- tavarua_search(radio, ctrl->value, SRCH_DIR_UP);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_STATE:
- /* check if already on */
- radio->handle_irq = 1;
- if (((ctrl->value == FM_RECV) || (ctrl->value == FM_TRANS))
- && !(radio->registers[RDCTRL] &
- ctrl->value)) {
- FMDBG("clearing flags\n");
- init_completion(&radio->sync_xfr_start);
- init_completion(&radio->sync_req_done);
- radio->xfr_in_progress = 0;
- radio->xfr_bytes_left = 0;
- FMDBG("turning on ..\n");
- retval = tavarua_start(radio, ctrl->value);
- if (retval >= 0) {
- /* Enabling 'SoftMute' & 'SignalBlending' */
- value = (radio->registers[IOCTRL] |
- IOC_SFT_MUTE | IOC_SIG_BLND);
- retval = tavarua_write_register(radio,
- IOCTRL, value);
- if (retval < 0)
- FMDBG("SMute and SBlending"
- "not enabled\n");
- }
- }
- /* check if off */
- else if ((ctrl->value == FM_OFF) && radio->registers[RDCTRL]) {
- radio->spur_table_size = 0;
- FMDBG("%s: turning off...\n", __func__);
- tavarua_write_register(radio, RDCTRL, ctrl->value);
- /* flush the event and work queues */
- kfifo_reset(&radio->data_buf[TAVARUA_BUF_EVENTS]);
- flush_workqueue(radio->wqueue);
- /*
- * queue the READY event from the host side
- * in case of FM off
- */
- tavarua_q_event(radio, TAVARUA_EVT_RADIO_DISABLED);
- FMDBG("%s, Disable All Interrupts\n", __func__);
- /* disable irq */
- dis_buf[STATUS_REG1] = 0x00;
- dis_buf[STATUS_REG2] = 0x00;
- dis_buf[STATUS_REG3] = TRANSFER;
- retval = sync_write_xfr(radio, INT_CTRL, dis_buf);
- if (retval < 0) {
- pr_err("%s: failed to disable"
- "Interrupts\n", __func__);
- return retval;
- }
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH:
- FMDBG("Setting audio path ...\n");
- if (ctrl->value == FM_DIGITAL_PATH) {
- FMDBG("Digital audio path enabled ...\n");
- retval = tavarua_set_audio_path(
- TAVARUA_AUDIO_OUT_DIGITAL_ON,
- TAVARUA_AUDIO_OUT_ANALOG_OFF);
- if (retval < 0) {
- FMDERR("Error in tavarua_set_audio_path"
- " %d\n", retval);
- }
- } else if (ctrl->value == FM_ANALOG_PATH) {
- FMDBG("Analog audio path enabled ...\n");
- retval = tavarua_set_audio_path(
- TAVARUA_AUDIO_OUT_DIGITAL_OFF,
- TAVARUA_AUDIO_OUT_ANALOG_ON);
- if (retval < 0) {
- FMDERR("Error in tavarua_set_audio_path"
- " %d\n", retval);
- }
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM:
- radio->enable_optimized_srch_alg = ctrl->value;
- FMDBG("V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM : %d",
- radio->enable_optimized_srch_alg);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_REGION:
- retval = tavarua_set_region(radio, ctrl->value);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
- retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
- FMDERR("sync_read_xfr [retval=%d]\n", retval);
- break;
- }
- /* RMSSI Threshold is a signed 8 bit value */
- xfr_buf[0] = (unsigned char)ctrl->value;
- xfr_buf[1] = (unsigned char)ctrl->value;
- retval = sync_write_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
- FMDERR("sync_write_xfr [retval=%d]\n", retval);
- break;
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
- radio->srch_params.srch_pty = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
- radio->srch_params.srch_pi = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
- radio->srch_params.preset_num = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
- radio->region_params.emphasis = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
- radio->region_params.rds_std = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_SPACING:
- radio->region_params.spacing = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSON:
- retval = 0;
- if (ctrl->value != (radio->registers[RDSCTRL] & RDS_ON)) {
- value = radio->registers[RDSCTRL] | ctrl->value;
- retval = tavarua_write_register(radio, RDSCTRL, value);
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- if (retval < 0)
- break;
- xfr_buf[8] = (ctrl->value & 0xFF000000) >> 24;
- xfr_buf[9] = (ctrl->value & 0x00FF0000) >> 16;
- xfr_buf[10] = (ctrl->value & 0x0000FF00) >> 8;
- xfr_buf[11] = (ctrl->value & 0x000000FF);
- retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
- value = radio->registers[ADVCTRL] | ctrl->value;
- retval = tavarua_write_register(radio, ADVCTRL, value);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_AF_JUMP:
- retval = tavarua_read_registers(radio, ADVCTRL, 1);
- SET_REG_FIELD(radio->registers[ADVCTRL], ctrl->value,
- RDSAF_OFFSET, RDSAF_MASK);
- msleep(TAVARUA_DELAY*5);
- retval = tavarua_write_register(radio,
- ADVCTRL, radio->registers[ADVCTRL]);
- msleep(TAVARUA_DELAY*5);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA:
- retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
- FMDERR("sync_read_xfr [retval=%d]\n", retval);
- break;
- }
- xfr_buf[4] = (unsigned char)ctrl->value;
- retval = sync_write_xfr(radio, RX_CONFIG, xfr_buf);
- if (retval < 0) {
- FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
- FMDERR("sync_write_xfr [retval=%d]\n", retval);
- break;
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_HLSI:
- retval = tavarua_read_registers(radio, RDCTRL, 1);
- SET_REG_FIELD(radio->registers[RDCTRL], ctrl->value,
- RDCTRL_HLSI_OFFSET, RDCTRL_HLSI_MASK);
- retval = tavarua_write_register(radio, RDCTRL,
- radio->registers[RDCTRL]);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- if (retval < 0)
- break;
- xfr_buf[1] = ctrl->value;
- retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_PSALL:
- retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
- value = ctrl->value & RDS_CONFIG_PSALL;
- if (retval < 0)
- break;
- xfr_buf[12] &= ~RDS_CONFIG_PSALL;
- xfr_buf[12] |= value;
- retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
- break;
- case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
- retval = 0;
- if (ctrl->value == radio->lp_mode)
- break;
- if (ctrl->value) {
- FMDBG("going into low power mode\n");
- retval = tavarua_disable_interrupts(radio);
- } else {
- FMDBG("going into normal power mode\n");
- tavarua_setup_interrupts(radio,
- (radio->registers[RDCTRL] & 0x03));
- }
- break;
- case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
- SET_REG_FIELD(radio->registers[IOCTRL], ctrl->value,
- IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
- break;
- case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
- size = 0x04;
- /* Poking the value of ON Channel Threshold value */
- xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
- xfr_buf[1] = ON_CHANNEL_TH_MSB;
- xfr_buf[2] = ON_CHANNEL_TH_LSB;
- /* Data to be poked into the register */
- xfr_buf[3] = (ctrl->value & 0xFF000000) >> 24;
- xfr_buf[4] = (ctrl->value & 0x00FF0000) >> 16;
- xfr_buf[5] = (ctrl->value & 0x0000FF00) >> 8;
- xfr_buf[6] = (ctrl->value & 0x000000FF);
- for (cnt = 3; cnt < 7; cnt++) {
- FMDBG("On-channel data to be poked is : %d",
- (int)xfr_buf[cnt]);
- }
- retval = tavarua_write_registers(radio, XFRCTRL,
- xfr_buf, size+3);
- if (retval < 0) {
- pr_err("%s: Failed to write\n", __func__);
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- break;
- case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
- size = 0x04;
- /* Poking the value of OFF Channel Threshold value */
- xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
- xfr_buf[1] = OFF_CHANNEL_TH_MSB;
- xfr_buf[2] = OFF_CHANNEL_TH_LSB;
- /* Data to be poked into the register */
- xfr_buf[3] = (ctrl->value & 0xFF000000) >> 24;
- xfr_buf[4] = (ctrl->value & 0x00FF0000) >> 16;
- xfr_buf[5] = (ctrl->value & 0x0000FF00) >> 8;
- xfr_buf[6] = (ctrl->value & 0x000000FF);
- for (cnt = 3; cnt < 7; cnt++) {
- FMDBG("Off-channel data to be poked is : %d",
- (int)xfr_buf[cnt]);
- }
- retval = tavarua_write_registers(radio, XFRCTRL,
- xfr_buf, size+3);
- if (retval < 0) {
- pr_err("%s: Failed to write\n", __func__);
- return retval;
- }
- /*Wait for the XFR interrupt */
- msleep(TAVARUA_DELAY*10);
- break;
- /* TX Controls */
- case V4L2_CID_RDS_TX_PTY: {
- radio->pty = ctrl->value;
- } break;
- case V4L2_CID_RDS_TX_PI: {
- radio->pi = ctrl->value;
- } break;
- case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME: {
- FMDBG("In STOP_RDS_TX_PS_NAME\n");
- /*Pass a sample PS string */
- memset(tx_data, '0', XFR_REG_NUM);
- FMDBG("Writing PS header\n");
- retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
- FMDBG("retval of PS Header write: %d", retval);
- } break;
- case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT: {
- memset(tx_data, '0', XFR_REG_NUM);
- FMDBG("Writing RT header\n");
- retval = sync_write_xfr(radio, RDS_RT_0, tx_data);
- FMDBG("retval of Header write: %d", retval);
- } break;
- case V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT: {
- radio->ps_repeatcount = ctrl->value;
- } break;
- case V4L2_CID_TUNE_POWER_LEVEL: {
- unsigned char tx_power_lvl_config[FM_TX_PWR_LVL_MAX+1] = {
- 0x85, /* tx_da<5:3> = 0 lpf<2:0> = 5*/
- 0x95, /* tx_da<5:3> = 2 lpf<2:0> = 5*/
- 0x9D, /* tx_da<5:3> = 3 lpf<2:0> = 5*/
- 0xA5, /* tx_da<5:3> = 4 lpf<2:0> = 5*/
- 0xAD, /* tx_da<5:3> = 5 lpf<2:0> = 5*/
- 0xB5, /* tx_da<5:3> = 6 lpf<2:0> = 5*/
- 0xBD, /* tx_da<5:3> = 7 lpf<2:0> = 5*/
- 0xBF /* tx_da<5:3> = 7 lpf<2:0> = 7*/
- };
- if (ctrl->value > FM_TX_PWR_LVL_MAX)
- ctrl->value = FM_TX_PWR_LVL_MAX;
- if (ctrl->value < FM_TX_PWR_LVL_0)
- ctrl->value = FM_TX_PWR_LVL_0;
- retval = sync_read_xfr(radio, PHY_TXGAIN, xfr_buf);
- FMDBG("return for PHY_TXGAIN is %d", retval);
- if (retval < 0) {
- FMDBG("read failed");
- break;
- }
- xfr_buf[2] = tx_power_lvl_config[ctrl->value];
- retval = sync_write_xfr(radio, PHY_TXGAIN, xfr_buf);
- FMDBG("return for write PHY_TXGAIN is %d", retval);
- if (retval < 0)
- FMDBG("write failed");
- } break;
- case V4L2_CID_PRIVATE_SOFT_MUTE:
- radio->registers[IOCTRL] &= ~(IOC_SFT_MUTE);
- if (ctrl->value)
- radio->registers[IOCTRL] |= IOC_SFT_MUTE;
- retval = tavarua_write_register(radio, IOCTRL,
- radio->registers[IOCTRL]);
- if (retval < 0)
- FMDERR("Failed to enable/disable SMute\n");
- break;
- /*These IOCTL's are place holders to keep the
- driver compatible with change in frame works for IRIS */
- case V4L2_CID_PRIVATE_RIVA_ACCS_ADDR:
- case V4L2_CID_PRIVATE_RIVA_ACCS_LEN:
- case V4L2_CID_PRIVATE_RIVA_PEEK:
- case V4L2_CID_PRIVATE_RIVA_POKE:
- case V4L2_CID_PRIVATE_SSBI_ACCS_ADDR:
- case V4L2_CID_PRIVATE_SSBI_PEEK:
- case V4L2_CID_PRIVATE_SSBI_POKE:
- case V4L2_CID_PRIVATE_RDS_GRP_COUNTERS:
- case V4L2_CID_PRIVATE_SET_NOTCH_FILTER:
- case V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION:
- case V4L2_CID_PRIVATE_SINR_THRESHOLD:
- case V4L2_CID_PRIVATE_SINR_SAMPLES:
- case V4L2_CID_PRIVATE_SPUR_SELECTION:
- retval = 0;
- break;
- case V4L2_CID_PRIVATE_SPUR_FREQ:
- radio->spur_data.freq[radio->spur_table_size] =
- ctrl->value;
- break;
- case V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI:
- radio->spur_data.rmssi[radio->spur_table_size++] =
- ctrl->value;
- break;
- case V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE:
- retval = update_spur_table(radio);
- break;
- case V4L2_CID_PRIVATE_TX_TONE:
- retval = 0;
- memset(xfr_buf, 0, sizeof(xfr_buf));
- switch (ctrl->value) {
- case ONE_KHZ_LR_EQUA_0DBFS:
- xfr_buf[TONE_CHANNEL_EN_AND_SCALING_BYTE]
- = TONE_LEFT_RIGHT_CH_ENABLED;
- xfr_buf[TONE_LEFT_FREQ_BYTE] = 0x01;
- xfr_buf[TONE_RIGHT_FREQ_BYTE] = 0x01;
- break;
- case ONE_KHZ_LEFTONLY_EQUA_0DBFS:
- xfr_buf[TONE_CHANNEL_EN_AND_SCALING_BYTE]
- = TONE_LEFT_CH_ENABLED;
- xfr_buf[TONE_LEFT_FREQ_BYTE] = 0x01;
- break;
- case ONE_KHZ_RIGHTONLY_EQUA_0DBFS:
- xfr_buf[TONE_CHANNEL_EN_AND_SCALING_BYTE]
- = TONE_RIGHT_CH_ENABLED;
- xfr_buf[TONE_RIGHT_FREQ_BYTE] = 0x01;
- break;
- case ONE_KHZ_LR_EQUA_l8DBFS:
- xfr_buf[TONE_CHANNEL_EN_AND_SCALING_BYTE]
- = (LSH_DATA(TONE_SCALE_IND_12,
- TONE_SCALING_SHIFT)
- | TONE_LEFT_RIGHT_CH_ENABLED);
- xfr_buf[TONE_LEFT_FREQ_BYTE] = 0x01;
- xfr_buf[TONE_RIGHT_FREQ_BYTE] = 0x01;
- break;
- case FIFTEEN_KHZ_LR_EQUA_l8DBFS:
- xfr_buf[TONE_CHANNEL_EN_AND_SCALING_BYTE]
- = (LSH_DATA(TONE_SCALE_IND_12,
- TONE_SCALING_SHIFT)
- | TONE_LEFT_RIGHT_CH_ENABLED);
- xfr_buf[TONE_LEFT_FREQ_BYTE] = 0x0F;
- xfr_buf[TONE_RIGHT_FREQ_BYTE] = 0x0F;
- break;
- default:
- retval = -1;
- FMDERR("tone generator value not valid\n");
- break;
- }
- if (retval >= 0) {
- xfr_buf[TONE_GEN_CTRL_BYTE] = 0x01;
- retval = sync_write_xfr(radio, XFR_EXT, xfr_buf);
- }
- if (retval < 0)
- FMDERR("Tone generator failed\n");
- break;
- case V4L2_CID_PRIVATE_VALID_CHANNEL:
- /* Do not notify the host of tune event */
- atomic_set(&radio->validate_channel, 1);
- FMDBG("Going into low power mode\n");
- retval = tavarua_disable_interrupts(radio);
- /*
- * Tune to 50KHz adjacent channel. If the requested station
- * falls in JAPAN band and on the lower band-limit, then the
- * adjacnet channel to be considered is 50KHz to the right side
- * of the requested station as firmware does not allows to tune
- * to frequency outside the range: 76000KHz to 108000KHz.
- */
- if (ctrl->value == REGION_JAPAN_STANDARD_BAND_LOW)
- freq = (ctrl->value + ADJ_CHANNEL_KHZ);
- else
- freq = (ctrl->value - ADJ_CHANNEL_KHZ);
- INIT_COMPLETION(radio->sync_req_done);
- retval = tavarua_set_freq(radio, (freq * TUNE_MULT));
- if (retval < 0) {
- FMDERR("Failed to tune to adjacent station\n");
- goto error;
- }
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout))) {
- FMDERR("Timeout: No Tune response\n");
- retval = -ETIMEDOUT;
- goto error;
- }
- /*
- * Wait for a minimum of 100ms for the firmware
- * to start collecting the MPX_DCC values
- */
- msleep(TAVARUA_DELAY * 10);
- /* Compute MPX_DCC of adjacent station */
- retval = compute_MPX_DCC(radio, &mpx_dcc);
- if (retval < 0) {
- FMDERR("Failed to get MPX_DCC of adjacent station\n");
- goto error;
- }
- /* Calculate the absolute value of MPX_DCC */
- prev = abs(mpx_dcc);
- /* Tune back to original station */
- INIT_COMPLETION(radio->sync_req_done);
- retval = tavarua_set_freq(radio, (ctrl->value * TUNE_MULT));
- if (retval < 0) {
- FMDERR("Failed to tune to requested station\n");
- goto error;
- }
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout))) {
- FMDERR("Timeout: No Tune response\n");
- retval = -ETIMEDOUT;
- goto error;
- }
- /*
- * Wait for a minimum of 100ms for the firmware
- * to start collecting the MPX_DCC values
- */
- msleep(TAVARUA_DELAY * 10);
- /* Compute MPX_DCC of current station */
- retval = compute_MPX_DCC(radio, &mpx_dcc);
- if (retval < 0) {
- FMDERR("Failed to get MPX_DCC of current station\n");
- goto error;
- }
- /* Calculate the absolute value of MPX_DCC */
- curr = abs(mpx_dcc);
- FMDBG("Going into normal power mode\n");
- tavarua_setup_interrupts(radio,
- (radio->registers[RDCTRL] & 0x03));
- FMDBG("Absolute MPX_DCC of current station : %lu\n", curr);
- FMDBG("Absolute MPX_DCC of adjacent station : %lu\n", prev);
- /*
- * For valid stations, the absolute MPX_DCC value will be within
- * the range 0 <= MPX_DCC <= 12566 and the MPX_DCC value of the
- * adjacent station will be greater than 20,000.
- */
- if ((curr <= MPX_DCC_LIMIT) &&
- (prev > MPX_DCC_UPPER_LIMIT)) {
- FMDBG("%d KHz is A VALID STATION!\n", ctrl->value);
- radio->is_station_valid = VALID_CHANNEL;
- } else {
- FMDBG("%d KHz is NOT A VALID STATION!\n", ctrl->value);
- radio->is_station_valid = INVALID_CHANNEL;
- }
- error:
- atomic_set(&radio->validate_channel, 0);
- break;
- default:
- retval = -EINVAL;
- }
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set control failed with %d, id : %d\n", retval, ctrl->id);
- return retval;
- }
- static int compute_MPX_DCC(struct tavarua_device *radio, int *val)
- {
- int DCC = 0, retval = 0;
- int MPX_DCC[3];
- unsigned char value;
- unsigned char xfr_buf[XFR_REG_NUM];
- /* Freeze the MPX_DCC value from changing */
- value = CTRL_ON;
- retval = xfr_rdwr_data(radio, XFR_WRITE, 1, MPX_DCC_BYPASS_REG, &value);
- if (retval < 0) {
- FMDERR("%s: Failed to freeze MPX_DCC\n", __func__);
- return retval;
- }
- /* Measure the MPX_DCC of current station. */
- retval = xfr_rdwr_data(radio, XFR_READ, 3, MPX_DCC_DATA_REG, xfr_buf);
- if (retval < 0) {
- FMDERR("%s: Failed to read MPX_DCC\n", __func__);
- return retval;
- }
- MPX_DCC[0] = xfr_buf[0];
- MPX_DCC[1] = xfr_buf[1];
- MPX_DCC[2] = xfr_buf[2];
- /*
- * Form the final MPX_DCC parameter
- * MPX_DCC[0] will form the LSB part
- * MPX_DCC[1] will be the middle part and 4 bits of
- * MPX_DCC[2] will be the MSB part of the 20-bit signed MPX_DCC
- */
- DCC = (LSH_DATA(MPX_DCC[2], 16) | LSH_DATA(MPX_DCC[1], 8) | MPX_DCC[0]);
- /* if bit-19 is '1',set remaining bits to '1' & make it -tive */
- if (DCC & 0x00080000)
- DCC |= 0xFFF00000;
- *val = DCC;
- /* Un-freeze the MPX_DCC value */
- value = CTRL_OFF;
- retval = xfr_rdwr_data(radio, XFR_WRITE, 1, 0x88C0, &value);
- if (retval < 0) {
- FMDERR("%s: Failed to un-freeze MPX_DCC\n", __func__);
- return retval;
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_g_tuner
- =============================================================================*/
- /**
- This function is called to get tuner attributes.
- NOTE:
- To query the attributes of a tuner, applications initialize the index field
- and zero out the reserved array of a struct v4l2_tuner and call the
- VIDIOC_G_TUNER ioctl with a pointer to this structure. Drivers fill the rest
- of the structure or return an EINVAL error code when the index is out of
- bounds. To enumerate all tuners applications shall begin at index zero,
- incrementing by one until the driver returns EINVAL.
- @param file: File descriptor returned by open().
- @param tuner: pointer to struct v4l2_tuner.
- @return On success 0 is returned, else error code.
- @return EINVAL: The struct v4l2_tuner index is out of bounds.
- */
- static int tavarua_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
- unsigned char xfr_buf[XFR_REG_NUM];
- char rmssi = 0;
- unsigned char size = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(tuner == NULL)) {
- FMDERR("%s:tuner is null", __func__);
- return -EINVAL;
- }
- if (tuner->index > 0)
- return -EINVAL;
- /* read status rssi */
- retval = tavarua_read_registers(radio, IOCTRL, 1);
- if (retval < 0)
- return retval;
- /* read RMSSI */
- size = 0x1;
- xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
- xfr_buf[1] = RMSSI_PEEK_MSB;
- xfr_buf[2] = RMSSI_PEEK_LSB;
- retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
- msleep(TAVARUA_DELAY*10);
- retval = tavarua_read_registers(radio, XFRDAT0, 3);
- rmssi = radio->registers[XFRDAT0];
- tuner->signal = rmssi;
- strcpy(tuner->name, "FM");
- tuner->type = V4L2_TUNER_RADIO;
- tuner->rangelow = radio->region_params.band_low;
- tuner->rangehigh = radio->region_params.band_high;
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- tuner->capability = V4L2_TUNER_CAP_LOW;
- /* Stereo indicator == Stereo (instead of Mono) */
- if (radio->registers[IOCTRL] & IOC_MON_STR)
- tuner->audmode = V4L2_TUNER_MODE_STEREO;
- else
- tuner->audmode = V4L2_TUNER_MODE_MONO;
- /* automatic frequency control: -1: freq to low, 1 freq to high */
- tuner->afc = 0;
- return 0;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_s_tuner
- =============================================================================*/
- /**
- This function is called to set tuner attributes. Used to set mono/stereo mode.
- NOTE:
- Tuners have two writable properties, the audio mode and the radio frequency.
- To change the audio mode, applications initialize the index, audmode and
- reserved fields and call the VIDIOC_S_TUNER ioctl. This will not change the
- current tuner, which is determined by the current video input. Drivers may
- choose a different audio mode if the requested mode is invalid or unsupported.
- Since this is a write-only ioctl, it does not return the actually selected
- audio mode.
- To change the radio frequency the VIDIOC_S_FREQUENCY ioctl is available.
- @param file: File descriptor returned by open().
- @param tuner: pointer to struct v4l2_tuner.
- @return On success 0 is returned, else error code.
- @return -EINVAL: The struct v4l2_tuner index is out of bounds.
- */
- static int tavarua_vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
- int audmode;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(tuner == NULL)) {
- FMDERR("%s:tuner is null", __func__);
- return -EINVAL;
- }
- if (tuner->index > 0)
- return -EINVAL;
- FMDBG("%s: set low to %d\n", __func__, tuner->rangelow);
- radio->region_params.band_low = tuner->rangelow;
- radio->region_params.band_high = tuner->rangehigh;
- if (tuner->audmode == V4L2_TUNER_MODE_MONO)
- /* Mono */
- audmode = (radio->registers[IOCTRL] | IOC_MON_STR);
- else
- /* Stereo */
- audmode = (radio->registers[IOCTRL] & ~IOC_MON_STR);
- retval = tavarua_write_register(radio, IOCTRL, audmode);
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set tuner failed with %d\n", retval);
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_g_frequency
- =============================================================================*/
- /**
- This function is called to get tuner or modulator radio frequency.
- NOTE:
- To get the current tuner or modulator radio frequency applications set the
- tuner field of a struct v4l2_frequency to the respective tuner or modulator
- number (only input devices have tuners, only output devices have modulators),
- zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
- pointer to this structure. The driver stores the current frequency in the
- frequency field.
- @param file: File descriptor returned by open().
- @param freq: pointer to struct v4l2_frequency. This will be set to the
- resultant
- frequency in 62.5 khz on success.
- @return On success 0 is returned, else error code.
- @return EINVAL: The tuner index is out of bounds or the value in the type
- field is wrong.
- */
- static int tavarua_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(freq == NULL)) {
- FMDERR("%s:freq is null", __func__);
- return -EINVAL;
- }
- freq->type = V4L2_TUNER_RADIO;
- return tavarua_get_freq(radio, freq);
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_s_frequency
- =============================================================================*/
- /**
- This function is called to set tuner or modulator radio frequency.
- NOTE:
- To change the current tuner or modulator radio frequency applications
- initialize the tuner, type and frequency fields, and the reserved array of
- a struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer
- to this structure. When the requested frequency is not possible the driver
- assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
- write-only ioctl, it does not return the actual new frequency.
- @param file: File descriptor returned by open().
- @param freq: pointer to struct v4l2_frequency.
- @return On success 0 is returned, else error code.
- @return EINVAL: The tuner index is out of bounds or the value in the type
- field is wrong.
- */
- static int tavarua_vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int retval = -1;
- struct v4l2_frequency getFreq;
- FMDBG("%s\n", __func__);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(freq == NULL)) {
- FMDERR("%s:freq is null", __func__);
- return -EINVAL;
- }
- if (freq->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- FMDBG("Calling tavarua_set_freq\n");
- INIT_COMPLETION(radio->sync_req_done);
- retval = tavarua_set_freq(radio, freq->frequency);
- if (retval < 0) {
- printk(KERN_WARNING DRIVER_NAME
- ": set frequency failed with %d\n", retval);
- } else {
- /* Wait for interrupt i.e. complete
- (&radio->sync_req_done); call */
- if (!wait_for_completion_timeout(&radio->sync_req_done,
- msecs_to_jiffies(wait_timeout))) {
- FMDERR("Timeout: No Tune response");
- retval = tavarua_get_freq(radio, &getFreq);
- radio->tune_req = 0;
- if (retval > 0) {
- if (getFreq.frequency == freq->frequency) {
- /** This is success, queut the event*/
- tavarua_q_event(radio,
- TAVARUA_EVT_TUNE_SUCC);
- return 0;
- } else {
- return -EIO;
- }
- }
- }
- }
- radio->tune_req = 0;
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_dqbuf
- =============================================================================*/
- /**
- This function is called to exchange a buffer with the driver.
- This is main buffer function, in essense its equivalent to a blocking
- read call.
- Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or
- displayed (output) buffer from the driver's outgoing queue. They just set
- the type and memory fields of a struct v4l2_buffer as above, when VIDIOC_DQBUF
- is called with a pointer to this structure the driver fills the remaining
- fields or returns an error code.
- NOTE:
- By default VIDIOC_DQBUF blocks when no buffer is in the outgoing queue.
- When the O_NONBLOCK flag was given to the open() function, VIDIOC_DQBUF
- returns immediately with an EAGAIN error code when no buffer is available.
- @param file: File descriptor returned by open().
- @param buffer: pointer to struct v4l2_buffer.
- @return On success 0 is returned, else error code.
- @return EAGAIN: Non-blocking I/O has been selected using O_NONBLOCK and no
- buffer was in the outgoing queue.
- @return EINVAL: The buffer type is not supported, or the index is out of
- bounds, or no buffers have been allocated yet, or the userptr or length are
- invalid.
- @return ENOMEM: Not enough physical or virtual memory was available to enqueue
- a user pointer buffer.
- @return EIO: VIDIOC_DQBUF failed due to an internal error. Can also indicate
- temporary problems like signal loss. Note the driver might dequeue an (empty)
- buffer despite returning an error, or even stop capturing.
- */
- static int tavarua_vidioc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buffer)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- enum tavarua_buf_t buf_type = -1;
- unsigned char buf_fifo[STD_BUF_SIZE] = {0};
- struct kfifo *data_fifo = NULL;
- unsigned char *buf = NULL;
- unsigned int len = 0, retval = -1;
- if ((radio == NULL) || (buffer == NULL)) {
- FMDERR("radio/buffer is NULL\n");
- return -ENXIO;
- }
- buf_type = buffer->index;
- buf = (unsigned char *)buffer->m.userptr;
- len = buffer->length;
- FMDBG("%s: requesting buffer %d\n", __func__, buf_type);
- if ((buf_type < TAVARUA_BUF_MAX) && (buf_type >= 0)) {
- data_fifo = &radio->data_buf[buf_type];
- if (buf_type == TAVARUA_BUF_EVENTS) {
- if (wait_event_interruptible(radio->event_queue,
- kfifo_len(data_fifo)) < 0) {
- return -EINTR;
- }
- }
- } else {
- FMDERR("invalid buffer type\n");
- return -EINVAL;
- }
- if (len <= STD_BUF_SIZE) {
- buffer->bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0],
- len, &radio->buf_lock[buf_type]);
- } else {
- FMDERR("kfifo_out_locked can not use len more than 128\n");
- return -EINVAL;
- }
- retval = copy_to_user(buf, &buf_fifo[0], buffer->bytesused);
- if (retval > 0) {
- FMDERR("Failed to copy %d bytes of data\n", retval);
- return -EAGAIN;
- }
- return retval;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_g_fmt_type_private
- =============================================================================*/
- /**
- This function is here to make the v4l2 framework happy.
- We cannot use private buffers without it.
- @param file: File descriptor returned by open().
- @param f: pointer to struct v4l2_format.
- @return On success 0 is returned, else error code.
- @return EINVAL: The tuner index is out of bounds or the value in the type
- field is wrong.
- */
- static int tavarua_vidioc_g_fmt_type_private(struct file *file, void *priv,
- struct v4l2_format *f)
- {
- return 0;
- }
- /*=============================================================================
- FUNCTION: tavarua_vidioc_s_hw_freq_seek
- =============================================================================*/
- /**
- This function is called to perform a hardware frequency seek.
- Start a hardware frequency seek from the current frequency. To do this
- applications initialize the tuner, type, seek_upward and wrap_around fields,
- and zero out the reserved array of a struct v4l2_hw_freq_seek and call the
- VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer to this structure.
- This ioctl is supported if the V4L2_CAP_HW_FREQ_SEEK capability is set.
- @param file: File descriptor returned by open().
- @param seek: pointer to struct v4l2_hw_freq_seek.
- @return On success 0 is returned, else error code.
- @return EINVAL: The tuner index is out of bounds or the value in the type
- field is wrong.
- @return EAGAIN: The ioctl timed-out. Try again.
- */
- static int tavarua_vidioc_s_hw_freq_seek(struct file *file, void *priv,
- struct v4l2_hw_freq_seek *seek)
- {
- struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
- int dir;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (unlikely(seek == NULL)) {
- FMDERR("%s:seek is null", __func__);
- return -EINVAL;
- }
- if (seek->seek_upward)
- dir = SRCH_DIR_UP;
- else
- dir = SRCH_DIR_DOWN;
- FMDBG("starting search\n");
- return tavarua_search(radio, CTRL_ON, dir);
- }
- /*
- * tavarua_viddev_tamples - video device interface
- */
- static const struct v4l2_ioctl_ops tavarua_ioctl_ops = {
- .vidioc_querycap = tavarua_vidioc_querycap,
- .vidioc_queryctrl = tavarua_vidioc_queryctrl,
- .vidioc_g_ctrl = tavarua_vidioc_g_ctrl,
- .vidioc_s_ctrl = tavarua_vidioc_s_ctrl,
- .vidioc_g_tuner = tavarua_vidioc_g_tuner,
- .vidioc_s_tuner = tavarua_vidioc_s_tuner,
- .vidioc_g_frequency = tavarua_vidioc_g_frequency,
- .vidioc_s_frequency = tavarua_vidioc_s_frequency,
- .vidioc_s_hw_freq_seek = tavarua_vidioc_s_hw_freq_seek,
- .vidioc_dqbuf = tavarua_vidioc_dqbuf,
- .vidioc_g_fmt_type_private = tavarua_vidioc_g_fmt_type_private,
- .vidioc_s_ext_ctrls = tavarua_vidioc_s_ext_ctrls,
- };
- static struct video_device tavarua_viddev_template = {
- .fops = &tavarua_fops,
- .ioctl_ops = &tavarua_ioctl_ops,
- .name = DRIVER_NAME,
- .release = video_device_release,
- };
- /*==============================================================
- FUNCTION: FmQSocCom_EnableInterrupts
- ==============================================================*/
- /**
- This function enable interrupts.
- @param radio: structure pointer passed by client.
- @param state: FM radio state (receiver/transmitter/off/reset).
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_setup_interrupts(struct tavarua_device *radio,
- enum radio_state_t state)
- {
- int retval;
- unsigned char int_ctrl[XFR_REG_NUM];
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (!radio->lp_mode)
- return 0;
- int_ctrl[STATUS_REG1] = READY | TUNE | SEARCH | SCANNEXT |
- SIGNAL | INTF | SYNC | AUDIO;
- if (state == FM_RECV)
- int_ctrl[STATUS_REG2] = RDSDAT | RDSRT | RDSPS | RDSAF;
- else
- int_ctrl[STATUS_REG2] = TXRDSDAT | TXRDSDONE;
- int_ctrl[STATUS_REG3] = TRANSFER | ERROR;
- /* use xfr for interrupt setup */
- if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
- || radio->chipID == BAHAMA_2_0 || radio->chipID == BAHAMA_2_1) {
- FMDBG("Setting interrupts\n");
- retval = sync_write_xfr(radio, INT_CTRL, int_ctrl);
- /* use register write to setup interrupts */
- } else {
- retval = tavarua_write_register(radio,
- STATUS_REG1, int_ctrl[STATUS_REG1]);
- if (retval < 0)
- return retval;
- retval = tavarua_write_register(radio,
- STATUS_REG2, int_ctrl[STATUS_REG2]);
- if (retval < 0)
- return retval;
- retval = tavarua_write_register(radio,
- STATUS_REG3, int_ctrl[STATUS_REG3]);
- if (retval < 0)
- return retval;
- }
- radio->lp_mode = 0;
- /* tavarua_handle_interrupts force reads all the interrupt status
- * registers and it is not valid for MBA 2.1
- */
- if ((radio->chipID != MARIMBA_2_1) && (radio->chipID != BAHAMA_1_0)
- && (radio->chipID != BAHAMA_2_0)
- && (radio->chipID != BAHAMA_2_1))
- tavarua_handle_interrupts(radio);
- return retval;
- }
- /*==============================================================
- FUNCTION: tavarua_disable_interrupts
- ==============================================================*/
- /**
- This function disables interrupts.
- @param radio: structure pointer passed by client.
- @return => 0 if successful.
- @return < 0 if failure.
- */
- static int tavarua_disable_interrupts(struct tavarua_device *radio)
- {
- unsigned char lpm_buf[XFR_REG_NUM];
- int retval;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (radio->lp_mode)
- return 0;
- FMDBG("%s\n", __func__);
- /* In Low power mode, disable all the interrupts that are not being
- waited by the Application */
- lpm_buf[STATUS_REG1] = TUNE | SEARCH | SCANNEXT;
- lpm_buf[STATUS_REG2] = 0x00;
- lpm_buf[STATUS_REG3] = TRANSFER;
- /* use xfr for interrupt setup */
- wait_timeout = 100;
- if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
- || radio->chipID == BAHAMA_2_0 || radio->chipID == BAHAMA_2_1)
- retval = sync_write_xfr(radio, INT_CTRL, lpm_buf);
- /* use register write to setup interrupts */
- else
- retval = tavarua_write_registers(radio, STATUS_REG1, lpm_buf,
- ARRAY_SIZE(lpm_buf));
- /*INT_CTL writes may fail with TIME_OUT as all the
- interrupts have been disabled
- */
- if (retval > -1 || retval == -ETIME) {
- radio->lp_mode = 1;
- /*Consider timeout as a valid case here*/
- retval = 0;
- }
- wait_timeout = WAIT_TIMEOUT;
- return retval;
- }
- /*==============================================================
- FUNCTION: tavarua_start
- ==============================================================*/
- /**
- Starts/enables the device (FM radio).
- @param radio: structure pointer passed by client.
- @param state: FM radio state (receiver/transmitter/off/reset).
- @return On success 0 is returned, else error code.
- */
- static int tavarua_start(struct tavarua_device *radio,
- enum radio_state_t state)
- {
- int retval;
- FMDBG("%s <%d>\n", __func__, state);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- /* set geographic region */
- radio->region_params.region = TAVARUA_REGION_US;
- /* set radio mode */
- retval = tavarua_write_register(radio, RDCTRL, state);
- if (retval < 0)
- return retval;
- /* wait for radio to init */
- msleep(RADIO_INIT_TIME);
- /* enable interrupts */
- tavarua_setup_interrupts(radio, state);
- /* default region is US */
- radio->region_params.band_low = US_LOW_BAND * FREQ_MUL;
- radio->region_params.band_high = US_HIGH_BAND * FREQ_MUL;
- return 0;
- }
- /*==============================================================
- FUNCTION: tavarua_suspend
- ==============================================================*/
- /**
- Save state and stop all devices in system.
- @param pdev: platform device to be suspended.
- @param state: Power state to put each device in.
- @return On success 0 is returned, else error code.
- */
- static int tavarua_suspend(struct platform_device *pdev, pm_message_t state)
- {
- struct tavarua_device *radio = platform_get_drvdata(pdev);
- int retval;
- int users = 0;
- printk(KERN_INFO DRIVER_NAME "%s: radio suspend\n\n", __func__);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (radio) {
- users = atomic_read(&radio->users);
- if (!users) {
- retval = tavarua_disable_interrupts(radio);
- if (retval < 0) {
- printk(KERN_INFO DRIVER_NAME
- "tavarua_suspend error %d\n", retval);
- return -EIO;
- }
- }
- }
- return 0;
- }
- /*==============================================================
- FUNCTION: tavarua_resume
- ==============================================================*/
- /**
- Restore state of each device in system.
- @param pdev: platform device to be resumed.
- @return On success 0 is returned, else error code.
- */
- static int tavarua_resume(struct platform_device *pdev)
- {
- struct tavarua_device *radio = platform_get_drvdata(pdev);
- int retval;
- int users = 0;
- printk(KERN_INFO DRIVER_NAME "%s: radio resume\n\n", __func__);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- if (radio) {
- users = atomic_read(&radio->users);
- if (!users) {
- retval = tavarua_setup_interrupts(radio,
- (radio->registers[RDCTRL] & 0x03));
- if (retval < 0) {
- FMDERR("Fails to write RDCTRL");
- msleep(TAVARUA_DELAY);
- retval = tavarua_setup_interrupts(radio,
- (radio->registers[RDCTRL] & 0x03));
- if (retval < 0) {
- FMDERR("Error in tavarua_resume %d\n",
- retval);
- return -EIO;
- }
- }
- }
- }
- return 0;
- }
- /*==============================================================
- FUNCTION: tavarua_set_audio_path
- ==============================================================*/
- /**
- This function will configure the audio path to and from the
- FM core.
- This interface is expected to be called from the multimedia
- driver's thread. This interface should only be called when
- the FM hardware is enabled. If the FM hardware is not
- currently enabled, this interface will return an error.
- @param digital_on: Digital audio from the FM core should be enabled/disbled.
- @param analog_on: Analog audio from the FM core should be enabled/disbled.
- @return On success 0 is returned, else error code.
- */
- int tavarua_set_audio_path(int digital_on, int analog_on)
- {
- struct tavarua_device *radio = private_data;
- int rx_on = radio->registers[RDCTRL] & FM_RECV;
- int retval = 0;
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- /* RX */
- FMDBG("%s: digital: %d analog: %d\n", __func__, digital_on, analog_on);
- if ((radio->pdata != NULL) && (radio->pdata->config_i2s_gpio != NULL)) {
- if (digital_on) {
- retval = radio->pdata->config_i2s_gpio(FM_I2S_ON);
- if (retval) {
- pr_err("%s: config_i2s_gpio failed\n",
- __func__);
- }
- } else {
- retval = radio->pdata->config_i2s_gpio(FM_I2S_OFF);
- if (retval) {
- pr_err("%s: config_i2s_gpio failed\n",
- __func__);
- }
- }
- }
- SET_REG_FIELD(radio->registers[AUDIOCTRL],
- ((rx_on && analog_on) ? 1 : 0),
- AUDIORX_ANALOG_OFFSET,
- AUDIORX_ANALOG_MASK);
- SET_REG_FIELD(radio->registers[AUDIOCTRL],
- ((rx_on && digital_on) ? 1 : 0),
- AUDIORX_DIGITAL_OFFSET,
- AUDIORX_DIGITAL_MASK);
- SET_REG_FIELD(radio->registers[AUDIOCTRL],
- (rx_on ? 0 : 1),
- AUDIOTX_OFFSET,
- AUDIOTX_MASK);
- /*
- I2S Master/Slave configuration:
- Setting the FM SoC as I2S Master/Slave
- 'false' - FM SoC is I2S Slave
- 'true' - FM SoC is I2S Master
- We get this infomation from the respective target's board file :
- MSM7x30 - FM SoC is I2S Slave
- MSM8x60 - FM SoC is I2S Slave
- MSM7x27A - FM SoC is I2S Master
- */
- if (!radio->pdata->is_fm_soc_i2s_master) {
- FMDBG("FM SoC is I2S Slave\n");
- SET_REG_FIELD(radio->registers[AUDIOCTRL],
- (0),
- I2SCTRL_OFFSET,
- I2SCTRL_MASK);
- } else {
- FMDBG("FM SoC is I2S Master\n");
- SET_REG_FIELD(radio->registers[AUDIOCTRL],
- (1),
- I2SCTRL_OFFSET,
- I2SCTRL_MASK);
- }
- FMDBG("%s: %x\n", __func__, radio->registers[AUDIOCTRL]);
- return tavarua_write_register(radio, AUDIOCTRL,
- radio->registers[AUDIOCTRL]);
- }
- /*==============================================================
- FUNCTION: tavarua_probe
- ==============================================================*/
- /**
- Once called this functions initiates, allocates resources and registers video
- tuner device with the v4l2 framework.
- NOTE:
- probe() should verify that the specified device hardware
- actually exists; sometimes platform setup code can't be sure. The probing
- can use device resources, including clocks, and device platform_data.
- @param pdev: platform device to be probed.
- @return On success 0 is returned, else error code.
- -ENOMEM in low memory cases
- */
- static int __init tavarua_probe(struct platform_device *pdev)
- {
- struct marimba_fm_platform_data *tavarua_pdata;
- struct tavarua_device *radio;
- int retval = 0;
- int i = 0, j = 0;
- FMDBG("%s: probe called\n", __func__);
- if (unlikely(pdev == NULL)) {
- FMDERR("%s:pdev is null", __func__);
- return -EINVAL;
- }
- /* private data allocation */
- radio = kzalloc(sizeof(struct tavarua_device), GFP_KERNEL);
- if (!radio) {
- retval = -ENOMEM;
- goto err_initial;
- }
- radio->marimba = platform_get_drvdata(pdev);
- tavarua_pdata = pdev->dev.platform_data;
- radio->pdata = tavarua_pdata;
- radio->dev = &pdev->dev;
- radio->wqueue = NULL;
- platform_set_drvdata(pdev, radio);
- /* video device allocation */
- radio->videodev = video_device_alloc();
- if (!radio->videodev)
- goto err_radio;
- /* initial configuration */
- memcpy(radio->videodev, &tavarua_viddev_template,
- sizeof(tavarua_viddev_template));
- /*allocate internal buffers for decoded rds and event buffer*/
- for (i = 0; i < TAVARUA_BUF_MAX; i++) {
- int kfifo_alloc_rc=0;
- spin_lock_init(&radio->buf_lock[i]);
- if (i == TAVARUA_BUF_RAW_RDS)
- kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
- rds_buf*3, GFP_KERNEL);
- else if (i == TAVARUA_BUF_RT_RDS)
- kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
- STD_BUF_SIZE * 2, GFP_KERNEL);
- else
- kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
- STD_BUF_SIZE, GFP_KERNEL);
- if (kfifo_alloc_rc!=0) {
- printk(KERN_ERR "%s: failed allocating buffers %d\n",
- __func__, kfifo_alloc_rc);
- retval = -ENOMEM;
- goto err_all;
- }
- }
- /* initializing the device count */
- atomic_set(&radio->users, 1);
- radio->xfr_in_progress = 0;
- radio->xfr_bytes_left = 0;
- for (j = 0; j < TAVARUA_XFR_MAX; j++)
- radio->pending_xfrs[j] = 0;
- /* init transmit data */
- radio->tx_mode = TAVARUA_TX_RT;
- /* Init RT and PS Tx datas*/
- radio->pty = 0;
- radio->pi = 0;
- radio->ps_repeatcount = 0;
- /* init search params */
- radio->srch_params.srch_pty = 0;
- radio->srch_params.srch_pi = 0;
- radio->srch_params.preset_num = 0;
- radio->srch_params.get_list = 0;
- /* radio initializes to low power mode */
- radio->lp_mode = 1;
- radio->handle_irq = 1;
- /* init lock */
- mutex_init(&radio->lock);
- /* init completion flags */
- init_completion(&radio->sync_xfr_start);
- init_completion(&radio->sync_req_done);
- radio->tune_req = 0;
- /* initialize wait queue for event read */
- init_waitqueue_head(&radio->event_queue);
- /* initialize wait queue for raw rds read */
- init_waitqueue_head(&radio->read_queue);
- video_set_drvdata(radio->videodev, radio);
- /*Start the worker thread for event handling and register read_int_stat
- as worker function*/
- radio->wqueue = create_singlethread_workqueue("kfmradio");
- if (!radio->wqueue) {
- retval = -ENOMEM;
- goto err_all;
- }
- /* register video device */
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
- if (retval != 0) {
- printk(KERN_WARNING DRIVER_NAME
- ": Could not register video device\n");
- goto err_all;
- }
- private_data = radio;
- return 0;
- err_all:
- video_device_release(radio->videodev);
- if (radio->wqueue)
- destroy_workqueue(radio->wqueue);
- for (i--; i >= 0; i--) {
- kfifo_free(&radio->data_buf[i]);
- }
- err_radio:
- kfree(radio);
- err_initial:
- return retval;
- }
- /*==============================================================
- FUNCTION: tavarua_remove
- ==============================================================*/
- /**
- Removes the device.
- @param pdev: platform device to be removed.
- @return On success 0 is returned, else error code.
- */
- static int __devexit tavarua_remove(struct platform_device *pdev)
- {
- int i;
- struct tavarua_device *radio = platform_get_drvdata(pdev);
- if (unlikely(radio == NULL)) {
- FMDERR("%s:radio is null", __func__);
- return -EINVAL;
- }
- /* disable irq */
- tavarua_disable_irq(radio);
- destroy_workqueue(radio->wqueue);
- video_unregister_device(radio->videodev);
- /* free internal buffers */
- for (i = 0; i < TAVARUA_BUF_MAX; i++)
- kfifo_free(&radio->data_buf[i]);
- /* free state struct */
- kfree(radio);
- platform_set_drvdata(pdev, NULL);
- return 0;
- }
- /*
- Platform drivers follow the standard driver model convention, where
- discovery/enumeration is handled outside the drivers, and drivers
- provide probe() and remove() methods. They support power management
- and shutdown notifications using the standard conventions.
- */
- static struct platform_driver tavarua_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "marimba_fm",
- },
- .remove = __devexit_p(tavarua_remove),
- .suspend = tavarua_suspend,
- .resume = tavarua_resume,
- }; /* platform device we're adding */
- /*************************************************************************
- * Module Interface
- ************************************************************************/
- /*==============================================================
- FUNCTION: radio_module_init
- ==============================================================*/
- /**
- Module entry - add a platform-level device.
- @return Returns zero if the driver registered and bound to a device, else
- returns a negative error code when the driver not registered.
- */
- static int __init radio_module_init(void)
- {
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
- return platform_driver_probe(&tavarua_driver, tavarua_probe);
- }
- /*==============================================================
- FUNCTION: radio_module_exit
- ==============================================================*/
- /**
- Module exit - removes a platform-level device.
- NOTE:
- Note that this function will also release all memory- and port-based
- resources owned by the device (dev->resource).
- @return none.
- */
- static void __exit radio_module_exit(void)
- {
- platform_driver_unregister(&tavarua_driver);
- }
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR(DRIVER_AUTHOR);
- MODULE_DESCRIPTION(DRIVER_DESC);
- MODULE_VERSION(DRIVER_VERSION);
- module_init(radio_module_init);
- module_exit(radio_module_exit);
|