1. 程式人生 > >fs4412開發板學習筆記(十五)

fs4412開發板學習筆記(十五)

音效卡驅動dts.
======
    wm8960:[email protected]1a{
            compatible="wlf,wm8960";
            reg = <0x1a>;
    };

    i2s0:[email protected]03830000 {
         compatible = "samsung,exynos4412-i2s";
         reg = <0x03830000 0x100>;
         dmas = <&pdma0 10
                 &pdma0 9
&pdma0 8>; dma-names = "tx", "rx", "tx-sec"; clocks = <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"
; samsung,supports-6ch; samsung,supports-rstclr; samsung,supports-secdai; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_0>; }; sound { compatible = "bva,implant-bva" samsung,i2s-controller = <&i2s0>; samsung,audio-codec = <&wm8960>; }; ============ static
int soc_bind_dai_link(struct snd_soc_card *card, int num) { /* Find CPU DAI from registered DAIs*/ list_for_each_entry(cpu_dai, &dai_list, list) { if (dai_link->cpu_of_node && (cpu_dai->dev->of_node != dai_link->cpu_of_node)) continue; if (dai_link->cpu_name && strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) continue; if (dai_link->cpu_dai_name && strcmp(cpu_dai->name, dai_link->cpu_dai_name)) continue; rtd->cpu_dai = cpu_dai; } printk("\n=========soc_bind_dai_link=============\n");//soc-core.c P903 /* Find CODEC from registered CODECs */ list_for_each_entry(codec, &codec_list, list) { if (dai_link->codec_of_node) { if (codec->dev->of_node != dai_link->codec_of_node) continue; } else { if (strcmp(codec->name, dai_link->codec_name)) continue; } rtd->codec = codec; /* * CODEC found, so find CODEC DAI from registered DAIs from * this CODEC */ printk("\n=========list_for_each_entry 903=============\n"); list_for_each_entry(codec_dai, &dai_list, list) { printk("\n===%s=====%s======\n",codec_dai->name,dai_link->codec_dai_name); if (codec->dev == codec_dai->dev && !strcmp(codec_dai->name, dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; } } if (!rtd->codec_dai) { dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", dai_link->codec_dai_name); return -EPROBE_DEFER; } } } 問題: [ 1.915000] wm8960 0-001a: (wm8960_probe)No platform data supplied 解決: 註冊裝置驅動的匯流排號應該用0而不是4 [ 1.925000] wm8960 0-001a: Failed to issue reset [ 1.930000] Failed to issue reset ret=-6 [ 1.930000] wm8960 0-001a: ASoC: failed to probe CODEC -6 [ 1.935000] soc-audio soc-audio: ASoC: failed to instantiate card -6 #define ENXIO 6 /* No such device or address */ [ 1.965000] regulator-dummy: disabling [ 1.600000] ==codec->dev!=NULL snd_soc_register_codec == [ 1.605000] ==codec->dev.platform_data!=NULL snd_soc_register_codec == [ 1.805000] soc-audio soc-audio: ASoC: CODEC wm8960.0-001a not registered [ 1.810000] platform soc-audio: Driver soc-audio requests probe deferral [ 1.910000] soc-audio: probe of soc-audio failed with error -22 1、如何檢視linux核心中註冊了那些i2c裝置。 2、i2c裝置和i2c驅動的匹配。 static int wm8960_probe(struct snd_soc_codec *codec) { ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type);//設定codec的讀寫等函式 || codec->write = hw_write;//soc-io.c 98 codec->read = hw_read; codec->control_data = regmap_init_i2c(to_i2c_client(codec->dev),&config); || struct regmap *regmap_init_i2c(struct i2c_client *i2c,const struct regmap_config *config)//regmap-i2c.c 110 { return regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config); } || struct regmap *regmap_init(struct device *dev,const struct regmap_bus *bus,void *bus_context,const struct regmap_config *config)//regmap.c 395 { map->bus = bus;//443 if (map->format.format_write) {//639 map->defer_caching = false; map->reg_write = _regmap_bus_formatted_write;//在這裡設定了下面要使用的map->reg_write函式。 printk("map->reg_write = _regmap_bus_formatted_write;\n"); } else if (map->format.format_val) { map->defer_caching = true; map->reg_write = _regmap_bus_raw_write; printk("map->reg_write = _regmap_bus_raw_write;\n"); } } ret = wm8960_reset(codec);//wm8960.c 1019 } || unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { dev_dbg(codec->dev, "write %x = %x\n", reg, val); trace_snd_soc_reg_write(codec, reg, val); return codec->write(codec, reg, val);//soc-core.c 2349 } || static int hw_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { ret=regmap_write(codec->control_data, reg, value);//soc-io.c 45 } || int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { ret = _regmap_write(map, reg, val);//regmap.c 1348 } || int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { ret=map->reg_write(context, reg, val);//regmap.c 1322 } || static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { ret = map->bus->write(map->bus_context, map->work_buf,map->format.buf_size);//regmap.c 1268 } || static int regmap_i2c_write(void *context, const void *data, size_t count)//regmap-i2c.c 18 { ret = i2c_master_send(i2c, data, count); } || int i2c_master_send(const struct i2c_client *client, const char *buf, int count)//i2c-core.c 1807 { ret = i2c_transfer(adap, &msg, 1);//<<==== ret=-6 } || int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { ret = __i2c_transfer(adap, msgs, num);//i2c-core.c 1788 } || int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { ret = adap->algo->master_xfer(adap, msgs, num);//i2c-core.c 1725 } || static const struct i2c_algorithm s3c24xx_i2c_algorithm = {//drivers/i2c/busses/i2c-s3c2410.c .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; || static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num);//第一次就等於-6 } } || static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);//drivers/i2c/busses/i2c-s3c2410.c 733 ret = i2c->msg_idx;在這裡將ret=-6 } || static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) { i2c->msg_idx = ret;//drivers/i2c/busses/i2c-s3c2410.c 195 } || static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { i2c_s3c_irq_nextbyte(i2c, stat);//drivers/i2c/busses/i2c-s3c2410.c 300 } ------------------------------------------------------------ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) { struct s3c24xx_i2c *i2c = dev_id; unsigned long status; unsigned long tmp; status = readl(i2c->regs + S3C2410_IICSTAT);//問題出在這裡,status=0xf1低4位應該為0 printk("\n--%s-----%d-----stat=0x%x\n",__FUNCTION__,__LINE__,status); if (status & S3C2410_IICSTAT_ARBITR) { /* deal with arbitration loss */ dev_err(i2c->dev, "deal with arbitration loss\n"); } if (i2c->state == STATE_IDLE) { dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); goto out; } /* pretty much this leaves us with the fact that we've * transmitted or received whatever byte we last sent */ printk("\n--%s-----%d-----stat=0x%x\n",__FUNCTION__,__LINE__,status);//問題出在這裡,status應該為0,但在這裡變為非零,導致下面的if語句成立。 i2c_s3c_irq_nextbyte(i2c, status); out: return IRQ_HANDLED; } || static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) { case STATE_START: if (iicstat & S3C2410_IICSTAT_LASTBIT &&!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); s3c24xx_i2c_stop(i2c, -ENXIO); //drivers/i2c/busses/i2c-s3c2410.c 435 printk("--%s-----%d-----\n",__FUNCTION__,__LINE__);// -ENXIO=-6最終在這裡出錯 goto out_ack; } }