Loading sound/soc/stm/stm32_sai.c +155 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ * details. */ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> Loading @@ -27,6 +28,16 @@ #include "stm32_sai.h" static LIST_HEAD(sync_providers); static DEFINE_MUTEX(sync_mutex); struct sync_provider { struct list_head link; struct device_node *node; int (*sync_conf)(void *data, int synco); void *data; }; static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; Loading @@ -41,23 +52,143 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) { int ret; /* Enable peripheral clock to allow GCR register access */ ret = clk_prepare_enable(sai->pclk); if (ret) { dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); return ret; } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); clk_disable_unprepare(sai->pclk); return 0; } static int stm32_sai_sync_conf_provider(void *data, int synco) { struct stm32_sai_data *sai = (struct stm32_sai_data *)data; u32 prev_synco; int ret; /* Enable peripheral clock to allow GCR register access */ ret = clk_prepare_enable(sai->pclk); if (ret) { dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); return ret; } dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", sai->pdev->dev.of_node->name, synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", sai->pdev->dev.of_node->name, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); clk_disable_unprepare(sai->pclk); return -EINVAL; } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); clk_disable_unprepare(sai->pclk); return 0; } static int stm32_sai_set_sync_provider(struct device_node *np, int synco) { struct sync_provider *provider; int ret; mutex_lock(&sync_mutex); list_for_each_entry(provider, &sync_providers, link) { if (provider->node == np) { ret = provider->sync_conf(provider->data, synco); mutex_unlock(&sync_mutex); return ret; } } mutex_unlock(&sync_mutex); /* SAI sync provider not found */ return -ENODEV; } static int stm32_sai_set_sync(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci) { int ret; /* Configure sync client */ stm32_sai_sync_conf_client(sai, synci); /* Configure sync provider */ ret = stm32_sai_set_sync_provider(np_provider, synco); return ret; } static int stm32_sai_sync_add_provider(struct platform_device *pdev, void *data) { struct sync_provider *sp; sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); if (!sp) return -ENOMEM; sp->node = of_node_get(pdev->dev.of_node); sp->data = data; sp->sync_conf = &stm32_sai_sync_conf_provider; mutex_lock(&sync_mutex); list_add(&sp->link, &sync_providers); mutex_unlock(&sync_mutex); return 0; } static void stm32_sai_sync_del_provider(struct device_node *np) { struct sync_provider *sp; mutex_lock(&sync_mutex); list_for_each_entry(sp, &sync_providers, link) { if (sp->node == np) { list_del(&sp->link); of_node_put(sp->node); break; } } mutex_unlock(&sync_mutex); } static int stm32_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; void __iomem *base; const struct of_device_id *of_id; int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); sai->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sai->base)) return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) Loading @@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; if (!STM_SAI_IS_F4(sai)) { sai->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(sai->pclk)) { dev_err(&pdev->dev, "missing bus clock pclk\n"); return PTR_ERR(sai->pclk); } } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); Loading Loading @@ -92,16 +231,27 @@ static int stm32_sai_probe(struct platform_device *pdev) reset_control_deassert(rst); } ret = stm32_sai_sync_add_provider(pdev, sai); if (ret < 0) return ret; sai->set_sync = &stm32_sai_set_sync; sai->pdev = pdev; platform_set_drvdata(pdev, sai); return of_platform_populate(np, NULL, NULL, &pdev->dev); ret = of_platform_populate(np, NULL, NULL, &pdev->dev); if (ret < 0) stm32_sai_sync_del_provider(np); return ret; } static int stm32_sai_remove(struct platform_device *pdev) { of_platform_depopulate(&pdev->dev); stm32_sai_sync_del_provider(pdev->dev.of_node); return 0; } Loading sound/soc/stm/stm32_sai.h +19 −3 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ * details. */ #include <linux/bitfield.h> /******************** SAI Register Map **************************************/ /* common register */ /* Global configuration register */ #define STM_SAI_GCR 0x00 /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ Loading @@ -37,12 +39,13 @@ /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 #define SAI_GCR_SYNCIN_WDTH 2 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) #define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) #define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ SAI_GCR_SYNCIN_MASK) #define SAI_GCR_SYNCOUT_SHIFT 4 #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) #define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) /******************* Bit definition for SAI_XCR1 register *******************/ #define SAI_XCR1_RX_TX_SHIFT 0 Loading Loading @@ -231,6 +234,12 @@ #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) enum stm32_sai_syncout { STM_SAI_SYNC_OUT_NONE, STM_SAI_SYNC_OUT_A, STM_SAI_SYNC_OUT_B, }; enum stm32_sai_version { SAI_STM32F4, SAI_STM32H7 Loading @@ -247,15 +256,22 @@ struct stm32_sai_conf { /** * struct stm32_sai_data - private data of SAI instance driver * @pdev: device data pointer * @base: common register bank virtual base address * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz * @version: SOC version * @irq: SAI interrupt line * @set_sync: pointer to synchro mode configuration callback */ struct stm32_sai_data { struct platform_device *pdev; void __iomem *base; struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; struct stm32_sai_conf *conf; int irq; int (*set_sync)(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci); }; sound/soc/stm/stm32_sai_sub.c +95 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,12 @@ #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") #define SAI_SYNC_NONE 0x0 #define SAI_SYNC_INTERNAL 0x1 #define SAI_SYNC_EXTERNAL 0x2 #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer Loading @@ -65,6 +71,7 @@ * @cpu_dai: DAI runtime data pointer * @substream: PCM substream data pointer * @pdata: SAI block parent data pointer * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init Loading @@ -73,6 +80,8 @@ * @master: SAI block mode flag. (true=master, false=slave) set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) * @synci: SAI block ext sync source (client setting). (SAI sync provider index) * @fs_length: frame synchronization length. depends on protocol settings * @slots: rx or tx slot number * @slot_width: rx or tx slot width in bits Loading @@ -88,6 +97,7 @@ struct stm32_sai_sub_data { struct snd_soc_dai *cpu_dai; struct snd_pcm_substream *substream; struct stm32_sai_data *pdata; struct device_node *np_sync_provider; struct clk *sai_ck; dma_addr_t phys_addr; unsigned int mclk_rate; Loading @@ -96,6 +106,8 @@ struct stm32_sai_sub_data { bool master; int fmt; int sync; int synco; int synci; int fs_length; int slots; int slot_width; Loading Loading @@ -387,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } /* Set slave mode if sub-block is synchronized with another SAI */ if (sai->sync) { dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); cr1 |= SAI_XCR1_SLAVE; sai->master = false; } cr1_mask |= SAI_XCR1_SLAVE; /* do not generate master by default */ Loading Loading @@ -749,6 +769,16 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) if (STM_SAI_IS_CAPTURE(sai)) cr1 |= SAI_XCR1_RX_TX; /* Configure synchronization */ if (sai->sync == SAI_SYNC_EXTERNAL) { /* Configure synchro client and provider */ sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, sai->synco, sai->synci); } cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); } Loading Loading @@ -835,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; struct of_phandle_args args; int ret; if (!np) return -ENODEV; Loading Loading @@ -868,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } /* Get synchronization property */ args.np = NULL; ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); if (ret < 0 && ret != -ENOENT) { dev_err(&pdev->dev, "Failed to get st,sync property\n"); return ret; } sai->sync = SAI_SYNC_NONE; if (args.np) { if (args.np == np) { dev_err(&pdev->dev, "%s sync own reference\n", np->name); of_node_put(args.np); return -EINVAL; } sai->np_sync_provider = of_get_parent(args.np); if (!sai->np_sync_provider) { dev_err(&pdev->dev, "%s parent node not found\n", np->name); of_node_put(args.np); return -ENODEV; } sai->sync = SAI_SYNC_INTERNAL; if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { if (!STM_SAI_HAS_EXT_SYNC(sai)) { dev_err(&pdev->dev, "External synchro not supported\n"); of_node_put(args.np); return -EINVAL; } sai->sync = SAI_SYNC_EXTERNAL; sai->synci = args.args[0]; if (sai->synci < 1 || (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { dev_err(&pdev->dev, "Wrong SAI index\n"); of_node_put(args.np); return -EINVAL; } if (of_property_match_string(args.np, "compatible", "st,stm32-sai-sub-a") >= 0) sai->synco = STM_SAI_SYNC_OUT_A; if (of_property_match_string(args.np, "compatible", "st,stm32-sai-sub-b") >= 0) sai->synco = STM_SAI_SYNC_OUT_B; if (!sai->synco) { dev_err(&pdev->dev, "Unknown SAI sub-block\n"); of_node_put(args.np); return -EINVAL; } } dev_dbg(&pdev->dev, "%s synchronized with %s\n", pdev->name, args.np->full_name); } of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); Loading Loading
sound/soc/stm/stm32_sai.c +155 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ * details. */ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> Loading @@ -27,6 +28,16 @@ #include "stm32_sai.h" static LIST_HEAD(sync_providers); static DEFINE_MUTEX(sync_mutex); struct sync_provider { struct list_head link; struct device_node *node; int (*sync_conf)(void *data, int synco); void *data; }; static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; Loading @@ -41,23 +52,143 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) { int ret; /* Enable peripheral clock to allow GCR register access */ ret = clk_prepare_enable(sai->pclk); if (ret) { dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); return ret; } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); clk_disable_unprepare(sai->pclk); return 0; } static int stm32_sai_sync_conf_provider(void *data, int synco) { struct stm32_sai_data *sai = (struct stm32_sai_data *)data; u32 prev_synco; int ret; /* Enable peripheral clock to allow GCR register access */ ret = clk_prepare_enable(sai->pclk); if (ret) { dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); return ret; } dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", sai->pdev->dev.of_node->name, synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", sai->pdev->dev.of_node->name, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); clk_disable_unprepare(sai->pclk); return -EINVAL; } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); clk_disable_unprepare(sai->pclk); return 0; } static int stm32_sai_set_sync_provider(struct device_node *np, int synco) { struct sync_provider *provider; int ret; mutex_lock(&sync_mutex); list_for_each_entry(provider, &sync_providers, link) { if (provider->node == np) { ret = provider->sync_conf(provider->data, synco); mutex_unlock(&sync_mutex); return ret; } } mutex_unlock(&sync_mutex); /* SAI sync provider not found */ return -ENODEV; } static int stm32_sai_set_sync(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci) { int ret; /* Configure sync client */ stm32_sai_sync_conf_client(sai, synci); /* Configure sync provider */ ret = stm32_sai_set_sync_provider(np_provider, synco); return ret; } static int stm32_sai_sync_add_provider(struct platform_device *pdev, void *data) { struct sync_provider *sp; sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); if (!sp) return -ENOMEM; sp->node = of_node_get(pdev->dev.of_node); sp->data = data; sp->sync_conf = &stm32_sai_sync_conf_provider; mutex_lock(&sync_mutex); list_add(&sp->link, &sync_providers); mutex_unlock(&sync_mutex); return 0; } static void stm32_sai_sync_del_provider(struct device_node *np) { struct sync_provider *sp; mutex_lock(&sync_mutex); list_for_each_entry(sp, &sync_providers, link) { if (sp->node == np) { list_del(&sp->link); of_node_put(sp->node); break; } } mutex_unlock(&sync_mutex); } static int stm32_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; void __iomem *base; const struct of_device_id *of_id; int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); sai->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sai->base)) return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) Loading @@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; if (!STM_SAI_IS_F4(sai)) { sai->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(sai->pclk)) { dev_err(&pdev->dev, "missing bus clock pclk\n"); return PTR_ERR(sai->pclk); } } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); Loading Loading @@ -92,16 +231,27 @@ static int stm32_sai_probe(struct platform_device *pdev) reset_control_deassert(rst); } ret = stm32_sai_sync_add_provider(pdev, sai); if (ret < 0) return ret; sai->set_sync = &stm32_sai_set_sync; sai->pdev = pdev; platform_set_drvdata(pdev, sai); return of_platform_populate(np, NULL, NULL, &pdev->dev); ret = of_platform_populate(np, NULL, NULL, &pdev->dev); if (ret < 0) stm32_sai_sync_del_provider(np); return ret; } static int stm32_sai_remove(struct platform_device *pdev) { of_platform_depopulate(&pdev->dev); stm32_sai_sync_del_provider(pdev->dev.of_node); return 0; } Loading
sound/soc/stm/stm32_sai.h +19 −3 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ * details. */ #include <linux/bitfield.h> /******************** SAI Register Map **************************************/ /* common register */ /* Global configuration register */ #define STM_SAI_GCR 0x00 /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ Loading @@ -37,12 +39,13 @@ /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 #define SAI_GCR_SYNCIN_WDTH 2 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) #define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) #define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ SAI_GCR_SYNCIN_MASK) #define SAI_GCR_SYNCOUT_SHIFT 4 #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) #define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) /******************* Bit definition for SAI_XCR1 register *******************/ #define SAI_XCR1_RX_TX_SHIFT 0 Loading Loading @@ -231,6 +234,12 @@ #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) enum stm32_sai_syncout { STM_SAI_SYNC_OUT_NONE, STM_SAI_SYNC_OUT_A, STM_SAI_SYNC_OUT_B, }; enum stm32_sai_version { SAI_STM32F4, SAI_STM32H7 Loading @@ -247,15 +256,22 @@ struct stm32_sai_conf { /** * struct stm32_sai_data - private data of SAI instance driver * @pdev: device data pointer * @base: common register bank virtual base address * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz * @version: SOC version * @irq: SAI interrupt line * @set_sync: pointer to synchro mode configuration callback */ struct stm32_sai_data { struct platform_device *pdev; void __iomem *base; struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; struct stm32_sai_conf *conf; int irq; int (*set_sync)(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci); };
sound/soc/stm/stm32_sai_sub.c +95 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,12 @@ #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") #define SAI_SYNC_NONE 0x0 #define SAI_SYNC_INTERNAL 0x1 #define SAI_SYNC_EXTERNAL 0x2 #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer Loading @@ -65,6 +71,7 @@ * @cpu_dai: DAI runtime data pointer * @substream: PCM substream data pointer * @pdata: SAI block parent data pointer * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init Loading @@ -73,6 +80,8 @@ * @master: SAI block mode flag. (true=master, false=slave) set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) * @synci: SAI block ext sync source (client setting). (SAI sync provider index) * @fs_length: frame synchronization length. depends on protocol settings * @slots: rx or tx slot number * @slot_width: rx or tx slot width in bits Loading @@ -88,6 +97,7 @@ struct stm32_sai_sub_data { struct snd_soc_dai *cpu_dai; struct snd_pcm_substream *substream; struct stm32_sai_data *pdata; struct device_node *np_sync_provider; struct clk *sai_ck; dma_addr_t phys_addr; unsigned int mclk_rate; Loading @@ -96,6 +106,8 @@ struct stm32_sai_sub_data { bool master; int fmt; int sync; int synco; int synci; int fs_length; int slots; int slot_width; Loading Loading @@ -387,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } /* Set slave mode if sub-block is synchronized with another SAI */ if (sai->sync) { dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); cr1 |= SAI_XCR1_SLAVE; sai->master = false; } cr1_mask |= SAI_XCR1_SLAVE; /* do not generate master by default */ Loading Loading @@ -749,6 +769,16 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) if (STM_SAI_IS_CAPTURE(sai)) cr1 |= SAI_XCR1_RX_TX; /* Configure synchronization */ if (sai->sync == SAI_SYNC_EXTERNAL) { /* Configure synchro client and provider */ sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, sai->synco, sai->synci); } cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); } Loading Loading @@ -835,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; struct of_phandle_args args; int ret; if (!np) return -ENODEV; Loading Loading @@ -868,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } /* Get synchronization property */ args.np = NULL; ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); if (ret < 0 && ret != -ENOENT) { dev_err(&pdev->dev, "Failed to get st,sync property\n"); return ret; } sai->sync = SAI_SYNC_NONE; if (args.np) { if (args.np == np) { dev_err(&pdev->dev, "%s sync own reference\n", np->name); of_node_put(args.np); return -EINVAL; } sai->np_sync_provider = of_get_parent(args.np); if (!sai->np_sync_provider) { dev_err(&pdev->dev, "%s parent node not found\n", np->name); of_node_put(args.np); return -ENODEV; } sai->sync = SAI_SYNC_INTERNAL; if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { if (!STM_SAI_HAS_EXT_SYNC(sai)) { dev_err(&pdev->dev, "External synchro not supported\n"); of_node_put(args.np); return -EINVAL; } sai->sync = SAI_SYNC_EXTERNAL; sai->synci = args.args[0]; if (sai->synci < 1 || (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { dev_err(&pdev->dev, "Wrong SAI index\n"); of_node_put(args.np); return -EINVAL; } if (of_property_match_string(args.np, "compatible", "st,stm32-sai-sub-a") >= 0) sai->synco = STM_SAI_SYNC_OUT_A; if (of_property_match_string(args.np, "compatible", "st,stm32-sai-sub-b") >= 0) sai->synco = STM_SAI_SYNC_OUT_B; if (!sai->synco) { dev_err(&pdev->dev, "Unknown SAI sub-block\n"); of_node_put(args.np); return -EINVAL; } } dev_dbg(&pdev->dev, "%s synchronized with %s\n", pdev->name, args.np->full_name); } of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); Loading