msm codec 代码跟踪

sound/soc/codecs/msm8x16-wcd.c

static struct spmi_device_id msm8x16_wcd_spmi_id_table[] = {
    {"wcd-spmi", MSM8X16_WCD_SPMI_DIGITAL},
    {"wcd-spmi", MSM8X16_WCD_SPMI_ANALOG},
    {}
};

static struct of_device_id msm8x16_wcd_spmi_of_match[] = {
    { .compatible = "qcom,msm8x16_wcd_codec",},
    { },
};

static struct spmi_driver wcd_spmi_driver = {
    .driver                 = {
        .owner          = THIS_MODULE,
        .name           = "wcd-spmi-core",
        .of_match_table = msm8x16_wcd_spmi_of_match
    },
#ifdef CONFIG_PM
        .suspend = msm8x16_wcd_spmi_suspend,
        .resume = msm8x16_wcd_spmi_resume,
#endif
    .id_table               = msm8x16_wcd_spmi_id_table,
    .probe                  = msm8x16_wcd_spmi_probe,
    .remove                 = msm8x16_wcd_spmi_remove,
};

static int __init msm8x16_wcd_codec_init(void)
{
    spmi_driver_register(&wcd_spmi_driver);
    return 0;
}

static int msm8x16_wcd_spmi_probe(struct spmi_device *spmi)
{
    int ret = 0;
    struct msm8x16_wcd *msm8x16 = NULL;
    struct msm8x16_wcd_pdata *pdata;
    struct resource *wcd_resource;
    int adsp_state;
    static int spmi_dev_registered_cnt;

    dev_dbg(&spmi->dev, "%s(%d):slave ID = 0x%x\n",
        __func__, __LINE__,  spmi->sid);

    adsp_state = apr_get_subsys_state();
    if (adsp_state != APR_SUBSYS_LOADED) {
        dev_dbg(&spmi->dev, "Adsp is not loaded yet %d\n",
                adsp_state);
        return -EPROBE_DEFER;
    }

    wcd_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
    if (!wcd_resource) {
        dev_err(&spmi->dev, "Unable to get Tombak base address\n");
        return -ENXIO;
    }

    switch (wcd_resource->start) {
    case TOMBAK_CORE_0_SPMI_ADDR:
        msm8x16_wcd_modules[0].spmi = spmi;
        msm8x16_wcd_modules[0].base = (spmi->sid << 16) +
                        wcd_resource->start;
        wcd9xxx_spmi_set_dev(msm8x16_wcd_modules[0].spmi, 0);
        device_init_wakeup(&spmi->dev, true);
        break;
    case TOMBAK_CORE_1_SPMI_ADDR:
        msm8x16_wcd_modules[1].spmi = spmi;
        msm8x16_wcd_modules[1].base = (spmi->sid << 16) +
                        wcd_resource->start;
        wcd9xxx_spmi_set_dev(msm8x16_wcd_modules[1].spmi, 1);
    if (wcd9xxx_spmi_irq_init()) {
        dev_err(&spmi->dev,
                "%s: irq initialization failed\n", __func__);
    } else {
        dev_dbg(&spmi->dev,
                "%s: irq initialization passed\n", __func__);
    }
        spmi_dev_registered_cnt++;
        goto register_codec;
    default:
        ret = -EINVAL;
        goto rtn;
    }


    dev_dbg(&spmi->dev, "%s(%d):start addr = 0x%pK\n",
        __func__, __LINE__,  &wcd_resource->start);

    if (wcd_resource->start != TOMBAK_CORE_0_SPMI_ADDR)
        goto rtn;

    if (spmi->dev.of_node) {
        dev_dbg(&spmi->dev, "%s:Platform data from device tree\n",
            __func__);
        // 解析设备树
        pdata = msm8x16_wcd_populate_dt_pdata(&spmi->dev);
        spmi->dev.platform_data = pdata;
    } else {
        dev_dbg(&spmi->dev, "%s:Platform data from board file\n",
            __func__);
        pdata = spmi->dev.platform_data;
    }
    if (pdata == NULL) {
        dev_err(&spmi->dev, "%s:Platform data failed to populate\n",
            __func__);
        goto rtn;
    }

    msm8x16 = kzalloc(sizeof(struct msm8x16_wcd), GFP_KERNEL);
    if (msm8x16 == NULL) {
        ret = -ENOMEM;
        goto rtn;
    }

    msm8x16->dev = &spmi->dev;
    msm8x16->read_dev = __msm8x16_wcd_reg_read;
    msm8x16->write_dev = __msm8x16_wcd_reg_write;
    ret = msm8x16_wcd_init_supplies(msm8x16, pdata);
    if (ret) {
        dev_err(&spmi->dev, "%s: Fail to enable Codec supplies\n",
            __func__);
        goto err_codec;
    }

    ret = msm8x16_wcd_enable_static_supplies(msm8x16, pdata);
    if (ret) {
        dev_err(&spmi->dev,
            "%s: Fail to enable Codec pre-reset supplies\n",
               __func__);
        goto err_codec;
    }
    usleep_range(5, 6);

    ret = msm8x16_wcd_device_init(msm8x16);
    if (ret) {
        dev_err(&spmi->dev,
            "%s:msm8x16_wcd_device_init failed with error %d\n",
            __func__, ret);
        goto err_supplies;
    }
    dev_set_drvdata(&spmi->dev, msm8x16);
    spmi_dev_registered_cnt++;
register_codec:
    if ((spmi_dev_registered_cnt == MAX_MSM8X16_WCD_DEVICE) && (!ret)) {
        if (msm8x16_wcd_modules[0].spmi) {
            // 注册codec
            ret = snd_soc_register_codec(
                    &msm8x16_wcd_modules[0].spmi->dev,
                    &soc_codec_dev_msm8x16_wcd,
                    msm8x16_wcd_i2s_dai,
                    ARRAY_SIZE(msm8x16_wcd_i2s_dai));
            if (ret) {
                dev_err(&spmi->dev,
                "%s:snd_soc_register_codec failed with error %d\n",
                __func__, ret);
                goto err_supplies;
            }
        }
    }
    return ret;
err_supplies:
    msm8x16_wcd_disable_supplies(msm8x16, pdata);
err_codec:
    kfree(msm8x16);
rtn:
    return ret;
}

// codec操作函数
static struct snd_soc_codec_driver soc_codec_dev_msm8x16_wcd = {
    .probe  = msm8x16_wcd_codec_probe,
    .remove = msm8x16_wcd_codec_remove,

    .read = msm8x16_wcd_read,
    .write = msm8x16_wcd_write,

    .suspend = msm8x16_wcd_suspend,
    .resume = msm8x16_wcd_resume,

    .readable_register = msm8x16_wcd_readable,
    .volatile_register = msm8x16_wcd_volatile,

    .reg_cache_size = MSM8X16_WCD_CACHE_SIZE,
    .reg_cache_default = msm8x16_wcd_reset_reg_defaults,
    .reg_word_size = 1,

    .controls = msm8x16_wcd_snd_controls,
    .num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls),
    .dapm_widgets = msm8x16_wcd_dapm_widgets,
    .num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets),
    .dapm_routes = audio_map,
    .num_dapm_routes = ARRAY_SIZE(audio_map),
};

static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[] = {
    {
        .name = "msm8x16_wcd_i2s_rx1",
        .id = AIF1_PB,
        .playback = {
            .stream_name = "AIF1 Playback",
            .rates = MSM8X16_WCD_RATES,
            .formats = MSM8X16_WCD_FORMATS,
            .rate_max = 192000,
            .rate_min = 8000,
            .channels_min = 1,
            .channels_max = 3,
        },
        .ops = &msm8x16_wcd_dai_ops,
    },
    {
        .name = "msm8x16_wcd_i2s_tx1",
        .id = AIF1_CAP,
        .capture = {
            .stream_name = "AIF1 Capture",
            .rates = MSM8X16_WCD_RATES,
            .formats = MSM8X16_WCD_FORMATS,
            .rate_max = 192000,
            .rate_min = 8000,
            .channels_min = 1,
            .channels_max = 4,
        },
        .ops = &msm8x16_wcd_dai_ops,
    },
    {
        .name = "cajon_vifeedback",
        .id = AIF2_VIFEED,
        .capture = {
            .stream_name = "VIfeed",
            .rates = MSM8X16_WCD_RATES,
            .formats = MSM8X16_WCD_FORMATS,
            .rate_max = 48000,
            .rate_min = 48000,
            .channels_min = 2,
            .channels_max = 2,
        },
        .ops = &msm8x16_wcd_dai_ops,
    },
};

sound/soc/soc-core.c

int snd_soc_register_codec(struct device *dev,
               const struct snd_soc_codec_driver *codec_drv,
               struct snd_soc_dai_driver *dai_drv,
               int num_dai)
{
    struct snd_soc_codec *codec;
    struct snd_soc_dai *dai;
    int ret, i;

    dev_dbg(dev, "codec register %s\n", dev_name(dev));

    codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
    if (codec == NULL)
        return -ENOMEM;

    codec->component.dapm_ptr = &codec->dapm;
    codec->component.codec = codec;

    ret = snd_soc_component_initialize(&codec->component,
            &codec_drv->component_driver, dev);
    if (ret)
        goto err_free;
    // 初始化
    if (codec_drv->controls) {
        codec->component.controls = codec_drv->controls;
        codec->component.num_controls = codec_drv->num_controls;
    }
    if (codec_drv->dapm_widgets) {
        codec->component.dapm_widgets = codec_drv->dapm_widgets;
        codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
    }
    if (codec_drv->dapm_routes) {
        codec->component.dapm_routes = codec_drv->dapm_routes;
        codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
    }

    if (codec_drv->probe)
        codec->component.probe = snd_soc_codec_drv_probe;
    if (codec_drv->remove)
        codec->component.remove = snd_soc_codec_drv_remove;
    if (codec_drv->write)
        codec->component.write = snd_soc_codec_drv_write;
    if (codec_drv->read)
        codec->component.read = snd_soc_codec_drv_read;
    codec->volatile_register = codec_drv->volatile_register;
    codec->readable_register = codec_drv->readable_register;
    codec->writable_register = codec_drv->writable_register;
    codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
    codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
    codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
    if (codec_drv->seq_notifier)
        codec->dapm.seq_notifier = codec_drv->seq_notifier;
    if (codec_drv->set_bias_level)
        codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
    codec->dev = dev;
    codec->driver = codec_drv;
    codec->component.val_bytes = codec_drv->reg_word_size;
    mutex_init(&codec->mutex);

#ifdef CONFIG_DEBUG_FS
    codec->component.init_debugfs = soc_init_codec_debugfs;
    codec->component.debugfs_prefix = "codec";
#endif

    if (codec_drv->get_regmap)
        codec->component.regmap = codec_drv->get_regmap(dev);

    for (i = 0; i < num_dai; i++) {
        fixup_codec_formats(&dai_drv[i].playback);
        fixup_codec_formats(&dai_drv[i].capture);
    }
    // 注册codec的dai
    ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
    if (ret < 0) {
        dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
        goto err_cleanup;
    }

    list_for_each_entry(dai, &codec->component.dai_list, list)
        dai->codec = codec;

    mutex_lock(&client_mutex);
    snd_soc_component_add_unlocked(&codec->component);
    list_add(&codec->list, &codec_list);
    mutex_unlock(&client_mutex);

    dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
        codec->component.name);
    return 0;

err_cleanup:
    snd_soc_component_cleanup(&codec->component);
err_free:
    kfree(codec);
    return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_codec);


static int snd_soc_register_dais(struct snd_soc_component *component,
    struct snd_soc_dai_driver *dai_drv, size_t count,
    bool legacy_dai_naming)
{
    struct device *dev = component->dev;
    struct snd_soc_dai *dai;
    unsigned int i;
    int ret;

    dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);

    component->dai_drv = dai_drv;
    component->num_dai = count;

    for (i = 0; i < count; i++) {

        dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
        if (dai == NULL) {
            ret = -ENOMEM;
            goto err;
        }

        /*
         * Back in the old days when we still had component-less DAIs,
         * instead of having a static name, component-less DAIs would
         * inherit the name of the parent device so it is possible to
         * register multiple instances of the DAI. We still need to keep
         * the same naming style even though those DAIs are not
         * component-less anymore.
         */
        if (count == 1 && legacy_dai_naming) {
            dai->name = fmt_single_name(dev, &dai->id);
        } else {
            dai->name = fmt_multiple_name(dev, &dai_drv[i]);
            if (dai_drv[i].id)
                dai->id = dai_drv[i].id;
            else
                dai->id = i;
        }
        if (dai->name == NULL) {
            kfree(dai);
            ret = -ENOMEM;
            goto err;
        }

        dai->component = component;
        dai->dev = dev;
        dai->driver = &dai_drv[i];
        if (!dai->driver->ops)
            dai->driver->ops = &null_dai_ops;

        list_add(&dai->list, &component->dai_list);

        dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
    }

    return 0;

err:
    snd_soc_unregister_dais(component);

    return ret;
}

猜你喜欢

转载自www.cnblogs.com/helloworldtoyou/p/10499777.html