From 420c0117db25db38b72b6230223f7a976d3070ea Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 17 Jun 2018 19:02:04 +0200 Subject: dmaengine: pxa: use a dma slave map In order to remove the specific knowledge of the dma mapping from PXA drivers, add a default slave map for pxa architectures. This won't impact MMP architecture, but is aimed only at all PXA boards. This is the first step, and once all drivers are converted, pxad_filter_fn() will be made static, and the DMA resources removed from device.c. Signed-off-by: Robert Jarzmik Reported-by: Arnd Bergmann Acked-by: Vinod Koul --- include/linux/platform_data/mmp_dma.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/platform_data/mmp_dma.h b/include/linux/platform_data/mmp_dma.h index d1397c8ed94e..6397b9c8149a 100644 --- a/include/linux/platform_data/mmp_dma.h +++ b/include/linux/platform_data/mmp_dma.h @@ -12,9 +12,13 @@ #ifndef MMP_DMA_H #define MMP_DMA_H +struct dma_slave_map; + struct mmp_dma_platdata { int dma_channels; int nb_requestors; + int slave_map_cnt; + const struct dma_slave_map *slave_map; }; #endif /* MMP_DMA_H */ -- cgit v1.3-6-gb490 From b6d1a17f4729e4fda5740a855da91d202db2c118 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 17 Jun 2018 19:02:14 +0200 Subject: dmaengine: pxa: document pxad_param Add some documentation for the pxad_param structure, and describe the contract behind the minimal required priority of a DMA channel. Signed-off-by: Robert Jarzmik Acked-by: Vinod Koul --- include/linux/dma/pxa-dma.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dma/pxa-dma.h b/include/linux/dma/pxa-dma.h index e56ec7af4fd7..9fc594f69eff 100644 --- a/include/linux/dma/pxa-dma.h +++ b/include/linux/dma/pxa-dma.h @@ -9,6 +9,15 @@ enum pxad_chan_prio { PXAD_PRIO_LOWEST, }; +/** + * struct pxad_param - dma channel request parameters + * @drcmr: requestor line number + * @prio: minimal mandatory priority of the channel + * + * If a requested channel is granted, its priority will be at least @prio, + * ie. if PXAD_PRIO_LOW is required, the requested channel will be either + * PXAD_PRIO_LOW, PXAD_PRIO_NORMAL or PXAD_PRIO_HIGHEST. + */ struct pxad_param { unsigned int drcmr; enum pxad_chan_prio prio; -- cgit v1.3-6-gb490 From cd31b80736852d34bc1072f3e579a6fd73a244e7 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 17 Jun 2018 19:02:17 +0200 Subject: ARM: pxa: change SSP DMA channels allocation Now the dma_slave_map is available for PXA architecture, switch the SSP device to it. This specifically means that : - for platform data based machines, the DMA requestor channels are extracted from the slave map, where pxa-ssp-dai. is a 1-1 match to ssp., and the channels are either "rx" or "tx". - for device tree platforms, the dma node should be hooked into the pxa2xx-ac97 or pxa-ssp-dai node. Signed-off-by: Robert Jarzmik Acked-by: Daniel Mack --- arch/arm/plat-pxa/ssp.c | 47 ---------------------------------------------- include/linux/pxa2xx_ssp.h | 2 -- sound/soc/pxa/pxa-ssp.c | 5 ++--- 3 files changed, 2 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index ba13f793fbce..ed36dcab80f1 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -127,53 +127,6 @@ static int pxa_ssp_probe(struct platform_device *pdev) if (IS_ERR(ssp->clk)) return PTR_ERR(ssp->clk); - if (dev->of_node) { - struct of_phandle_args dma_spec; - struct device_node *np = dev->of_node; - int ret; - - /* - * FIXME: we should allocate the DMA channel from this - * context and pass the channel down to the ssp users. - * For now, we lookup the rx and tx indices manually - */ - - /* rx */ - ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", - 0, &dma_spec); - - if (ret) { - dev_err(dev, "Can't parse dmas property\n"); - return -ENODEV; - } - ssp->drcmr_rx = dma_spec.args[0]; - of_node_put(dma_spec.np); - - /* tx */ - ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", - 1, &dma_spec); - if (ret) { - dev_err(dev, "Can't parse dmas property\n"); - return -ENODEV; - } - ssp->drcmr_tx = dma_spec.args[0]; - of_node_put(dma_spec.np); - } else { - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res == NULL) { - dev_err(dev, "no SSP RX DRCMR defined\n"); - return -ENODEV; - } - ssp->drcmr_rx = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res == NULL) { - dev_err(dev, "no SSP TX DRCMR defined\n"); - return -ENODEV; - } - ssp->drcmr_tx = res->start; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(dev, "no memory resource defined\n"); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 8461b18e4608..03a7ca46735b 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -212,8 +212,6 @@ struct ssp_device { int type; int use_count; int irq; - int drcmr_rx; - int drcmr_tx; struct device_node *of_node; }; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 6fc986080130..0b441338bdd4 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -105,9 +105,8 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - - dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "tx" : "rx"; snd_soc_dai_set_dma_data(cpu_dai, substream, dma); -- cgit v1.3-6-gb490 From 9fba738a53dda20e748d6ee240b6c017c8146b4b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 19 Jun 2018 16:41:41 +0200 Subject: clk: add duty cycle support Add the possibility to apply and query the clock signal duty cycle ratio. This is useful when the duty cycle of the clock signal depends on some other parameters controlled by the clock framework. For example, the duty cycle of a divider may depends on the raw divider setting (ratio = N / div) , which is controlled by the CCF. In such case, going through the pwm framework to control the duty cycle ratio of this clock would be a burden. A clock provider is not required to implement the operation to set and get the duty cycle. If it does not implement .get_duty_cycle(), the ratio is assumed to be 50%. This change also adds a new flag, CLK_DUTY_CYCLE_PARENT. This flag should be used to indicate that a clock, such as gates and muxes, may inherit the duty cycle ratio of its parent clock. If a clock does not provide a get_duty_cycle() callback and has CLK_DUTY_CYCLE_PARENT, then the call will be directly forwarded to its parent clock, if any. For set_duty_cycle(), the clock should also have CLK_SET_RATE_PARENT for the call to be forwarded Signed-off-by: Jerome Brunet Signed-off-by: Michael Turquette Link: lkml.kernel.org/r/20180619144141.8506-1-jbrunet@baylibre.com --- drivers/clk/clk.c | 199 +++++++++++++++++++++++++++++++++++++++++-- include/linux/clk-provider.h | 26 ++++++ include/linux/clk.h | 33 +++++++ include/trace/events/clk.h | 36 ++++++++ 4 files changed, 289 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9760b526ca31..b0a2719d86f3 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -68,6 +68,7 @@ struct clk_core { unsigned long max_rate; unsigned long accuracy; int phase; + struct clk_duty duty; struct hlist_head children; struct hlist_node child_node; struct hlist_head clks; @@ -2402,6 +2403,172 @@ int clk_get_phase(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_phase); +static void clk_core_reset_duty_cycle_nolock(struct clk_core *core) +{ + /* Assume a default value of 50% */ + core->duty.num = 1; + core->duty.den = 2; +} + +static int clk_core_update_duty_cycle_parent_nolock(struct clk_core *core); + +static int clk_core_update_duty_cycle_nolock(struct clk_core *core) +{ + struct clk_duty *duty = &core->duty; + int ret = 0; + + if (!core->ops->get_duty_cycle) + return clk_core_update_duty_cycle_parent_nolock(core); + + ret = core->ops->get_duty_cycle(core->hw, duty); + if (ret) + goto reset; + + /* Don't trust the clock provider too much */ + if (duty->den == 0 || duty->num > duty->den) { + ret = -EINVAL; + goto reset; + } + + return 0; + +reset: + clk_core_reset_duty_cycle_nolock(core); + return ret; +} + +static int clk_core_update_duty_cycle_parent_nolock(struct clk_core *core) +{ + int ret = 0; + + if (core->parent && + core->flags & CLK_DUTY_CYCLE_PARENT) { + ret = clk_core_update_duty_cycle_nolock(core->parent); + memcpy(&core->duty, &core->parent->duty, sizeof(core->duty)); + } else { + clk_core_reset_duty_cycle_nolock(core); + } + + return ret; +} + +static int clk_core_set_duty_cycle_parent_nolock(struct clk_core *core, + struct clk_duty *duty); + +static int clk_core_set_duty_cycle_nolock(struct clk_core *core, + struct clk_duty *duty) +{ + int ret; + + lockdep_assert_held(&prepare_lock); + + if (clk_core_rate_is_protected(core)) + return -EBUSY; + + trace_clk_set_duty_cycle(core, duty); + + if (!core->ops->set_duty_cycle) + return clk_core_set_duty_cycle_parent_nolock(core, duty); + + ret = core->ops->set_duty_cycle(core->hw, duty); + if (!ret) + memcpy(&core->duty, duty, sizeof(*duty)); + + trace_clk_set_duty_cycle_complete(core, duty); + + return ret; +} + +static int clk_core_set_duty_cycle_parent_nolock(struct clk_core *core, + struct clk_duty *duty) +{ + int ret = 0; + + if (core->parent && + core->flags & (CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT)) { + ret = clk_core_set_duty_cycle_nolock(core->parent, duty); + memcpy(&core->duty, &core->parent->duty, sizeof(core->duty)); + } + + return ret; +} + +/** + * clk_set_duty_cycle - adjust the duty cycle ratio of a clock signal + * @clk: clock signal source + * @num: numerator of the duty cycle ratio to be applied + * @den: denominator of the duty cycle ratio to be applied + * + * Apply the duty cycle ratio if the ratio is valid and the clock can + * perform this operation + * + * Returns (0) on success, a negative errno otherwise. + */ +int clk_set_duty_cycle(struct clk *clk, unsigned int num, unsigned int den) +{ + int ret; + struct clk_duty duty; + + if (!clk) + return 0; + + /* sanity check the ratio */ + if (den == 0 || num > den) + return -EINVAL; + + duty.num = num; + duty.den = den; + + clk_prepare_lock(); + + if (clk->exclusive_count) + clk_core_rate_unprotect(clk->core); + + ret = clk_core_set_duty_cycle_nolock(clk->core, &duty); + + if (clk->exclusive_count) + clk_core_rate_protect(clk->core); + + clk_prepare_unlock(); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_duty_cycle); + +static int clk_core_get_scaled_duty_cycle(struct clk_core *core, + unsigned int scale) +{ + struct clk_duty *duty = &core->duty; + int ret; + + clk_prepare_lock(); + + ret = clk_core_update_duty_cycle_nolock(core); + if (!ret) + ret = mult_frac(scale, duty->num, duty->den); + + clk_prepare_unlock(); + + return ret; +} + +/** + * clk_get_scaled_duty_cycle - return the duty cycle ratio of a clock signal + * @clk: clock signal source + * @scale: scaling factor to be applied to represent the ratio as an integer + * + * Returns the duty cycle ratio of a clock node multiplied by the provided + * scaling factor, or negative errno on error. + */ +int clk_get_scaled_duty_cycle(struct clk *clk, unsigned int scale) +{ + if (!clk) + return 0; + + return clk_core_get_scaled_duty_cycle(clk->core, scale); +} +EXPORT_SYMBOL_GPL(clk_get_scaled_duty_cycle); + /** * clk_is_match - check if two clk's point to the same hardware clock * @p: clk compared against q @@ -2455,12 +2622,13 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, if (!c) return; - seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %-3d\n", + seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %5d %6d\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, c->protect_count, clk_core_get_rate(c), clk_core_get_accuracy(c), - clk_core_get_phase(c)); + clk_core_get_phase(c), + clk_core_get_scaled_duty_cycle(c, 100000)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, @@ -2482,9 +2650,9 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk_core *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " enable prepare protect \n"); - seq_puts(s, " clock count count count rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); + seq_puts(s, " enable prepare protect duty\n"); + seq_puts(s, " clock count count count rate accuracy phase cycle\n"); + seq_puts(s, "---------------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -2511,6 +2679,8 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); + seq_printf(s, "\"duty_cycle\": %u", + clk_core_get_scaled_duty_cycle(c, 100000)); } static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) @@ -2572,6 +2742,7 @@ static const struct { ENTRY(CLK_SET_RATE_UNGATE), ENTRY(CLK_IS_CRITICAL), ENTRY(CLK_OPS_PARENT_ENABLE), + ENTRY(CLK_DUTY_CYCLE_PARENT), #undef ENTRY }; @@ -2610,6 +2781,17 @@ static int possible_parents_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(possible_parents); +static int clk_duty_cycle_show(struct seq_file *s, void *data) +{ + struct clk_core *core = s->private; + struct clk_duty *duty = &core->duty; + + seq_printf(s, "%u/%u\n", duty->num, duty->den); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle); + static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { struct dentry *root; @@ -2628,6 +2810,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) debugfs_create_u32("clk_enable_count", 0444, root, &core->enable_count); debugfs_create_u32("clk_protect_count", 0444, root, &core->protect_count); debugfs_create_u32("clk_notifier_count", 0444, root, &core->notifier_count); + debugfs_create_file("clk_duty_cycle", 0444, root, core, + &clk_duty_cycle_fops); if (core->num_parents > 1) debugfs_create_file("clk_possible_parents", 0444, root, core, @@ -2845,6 +3029,11 @@ static int __clk_core_init(struct clk_core *core) else core->phase = 0; + /* + * Set clk's duty cycle. + */ + clk_core_update_duty_cycle_nolock(core); + /* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index b7cfa037e593..08b1aa70a38d 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -38,6 +38,8 @@ #define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ /* parents need enable during gate/ungate, set rate and re-parent */ #define CLK_OPS_PARENT_ENABLE BIT(12) +/* duty cycle call may be forwarded to the parent clock */ +#define CLK_DUTY_CYCLE_PARENT BIT(13) struct clk; struct clk_hw; @@ -66,6 +68,17 @@ struct clk_rate_request { struct clk_hw *best_parent_hw; }; +/** + * struct clk_duty - Struture encoding the duty cycle ratio of a clock + * + * @num: Numerator of the duty cycle ratio + * @den: Denominator of the duty cycle ratio + */ +struct clk_duty { + unsigned int num; + unsigned int den; +}; + /** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers @@ -169,6 +182,15 @@ struct clk_rate_request { * by the second argument. Valid values for degrees are * 0-359. Return 0 on success, otherwise -EERROR. * + * @get_duty_cycle: Queries the hardware to get the current duty cycle ratio + * of a clock. Returned values denominator cannot be 0 and must be + * superior or equal to the numerator. + * + * @set_duty_cycle: Apply the duty cycle ratio to this clock signal specified by + * the numerator (2nd argurment) and denominator (3rd argument). + * Argument must be a valid ratio (denominator > 0 + * and >= numerator) Return 0 on success, otherwise -EERROR. + * * @init: Perform platform-specific initialization magic. * This is not not used by any of the basic clock types. * Please consider other ways of solving initialization problems @@ -218,6 +240,10 @@ struct clk_ops { unsigned long parent_accuracy); int (*get_phase)(struct clk_hw *hw); int (*set_phase)(struct clk_hw *hw, int degrees); + int (*get_duty_cycle)(struct clk_hw *hw, + struct clk_duty *duty); + int (*set_duty_cycle)(struct clk_hw *hw, + struct clk_duty *duty); void (*init)(struct clk_hw *hw); void (*debug_init)(struct clk_hw *hw, struct dentry *dentry); }; diff --git a/include/linux/clk.h b/include/linux/clk.h index 0dbd0885b2c2..4f750c481b82 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -141,6 +141,27 @@ int clk_set_phase(struct clk *clk, int degrees); */ int clk_get_phase(struct clk *clk); +/** + * clk_set_duty_cycle - adjust the duty cycle ratio of a clock signal + * @clk: clock signal source + * @num: numerator of the duty cycle ratio to be applied + * @den: denominator of the duty cycle ratio to be applied + * + * Adjust the duty cycle of a clock signal by the specified ratio. Returns 0 on + * success, -EERROR otherwise. + */ +int clk_set_duty_cycle(struct clk *clk, unsigned int num, unsigned int den); + +/** + * clk_get_duty_cycle - return the duty cycle ratio of a clock signal + * @clk: clock signal source + * @scale: scaling factor to be applied to represent the ratio as an integer + * + * Returns the duty cycle ratio multiplied by the scale provided, otherwise + * returns -EERROR. + */ +int clk_get_scaled_duty_cycle(struct clk *clk, unsigned int scale); + /** * clk_is_match - check if two clk's point to the same hardware clock * @p: clk compared against q @@ -183,6 +204,18 @@ static inline long clk_get_phase(struct clk *clk) return -ENOTSUPP; } +static inline int clk_set_duty_cycle(struct clk *clk, unsigned int num, + unsigned int den) +{ + return -ENOTSUPP; +} + +static inline unsigned int clk_get_scaled_duty_cycle(struct clk *clk, + unsigned int scale) +{ + return 0; +} + static inline bool clk_is_match(const struct clk *p, const struct clk *q) { return p == q; diff --git a/include/trace/events/clk.h b/include/trace/events/clk.h index 2cd449328aee..9004ffff7f32 100644 --- a/include/trace/events/clk.h +++ b/include/trace/events/clk.h @@ -192,6 +192,42 @@ DEFINE_EVENT(clk_phase, clk_set_phase_complete, TP_ARGS(core, phase) ); +DECLARE_EVENT_CLASS(clk_duty_cycle, + + TP_PROTO(struct clk_core *core, struct clk_duty *duty), + + TP_ARGS(core, duty), + + TP_STRUCT__entry( + __string( name, core->name ) + __field( unsigned int, num ) + __field( unsigned int, den ) + ), + + TP_fast_assign( + __assign_str(name, core->name); + __entry->num = duty->num; + __entry->den = duty->den; + ), + + TP_printk("%s %u/%u", __get_str(name), (unsigned int)__entry->num, + (unsigned int)__entry->den) +); + +DEFINE_EVENT(clk_duty_cycle, clk_set_duty_cycle, + + TP_PROTO(struct clk_core *core, struct clk_duty *duty), + + TP_ARGS(core, duty) +); + +DEFINE_EVENT(clk_duty_cycle, clk_set_duty_cycle_complete, + + TP_PROTO(struct clk_core *core, struct clk_duty *duty), + + TP_ARGS(core, duty) +); + #endif /* _TRACE_CLK_H */ /* This part must be outside protection */ -- cgit v1.3-6-gb490 From 05739375f1c0a1048fea8b9c4cb54d9e4a891938 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 29 Jun 2018 14:59:40 +0200 Subject: ASoC: pxa-ssp: remove .set_pll() and .set_clkdiv() callbacks The .set_pll() and .set_clkdiv() callbacks are considered legacy and should not be used anymore. In order to support PXA boards on DT platforms, remove them and let the code figure out the correct dividers and PLL base frequencies itself. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- include/linux/pxa2xx_ssp.h | 8 +++ sound/soc/pxa/pxa-ssp.c | 146 ++++++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 03a7ca46735b..13b4244d44c1 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -171,6 +171,14 @@ #define SSACD_SCDB (1 << 3) /* SSPSYSCLK Divider Bypass */ #define SSACD_ACPS(x) ((x) << 4) /* Audio clock PLL select */ #define SSACD_ACDS(x) ((x) << 0) /* Audio clock divider select */ +#define SSACD_ACDS_1 (0) +#define SSACD_ACDS_2 (1) +#define SSACD_ACDS_4 (2) +#define SSACD_ACDS_8 (3) +#define SSACD_ACDS_16 (4) +#define SSACD_ACDS_32 (5) +#define SSACD_SCDB_4X (0) +#define SSACD_SCDB_1X (1) #define SSACD_SCDX8 (1 << 7) /* SYSCLK division ratio select */ /* LPSS SSP */ diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 01d54697ede4..f8339bb01251 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -41,6 +41,7 @@ */ struct ssp_priv { struct ssp_device *ssp; + unsigned long ssp_clk; unsigned int sysclk; unsigned int dai_fmt; unsigned int configured_dai_fmt; @@ -192,21 +193,6 @@ static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) pxa_ssp_write_reg(ssp, SSCR0, sscr0); } -/** - * pxa_ssp_get_clkdiv - get SSP clock divider - */ -static u32 pxa_ssp_get_scr(struct ssp_device *ssp) -{ - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - u32 div; - - if (ssp->type == PXA25x_SSP) - div = ((sscr0 >> 8) & 0xff) * 2 + 2; - else - div = ((sscr0 >> 8) & 0xfff) + 1; - return div; -} - /* * Set the SSP ports SYSCLK. */ @@ -262,67 +248,18 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, return 0; } -/* - * Set the SSP clock dividers. - */ -static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); - struct ssp_device *ssp = priv->ssp; - int val; - - switch (div_id) { - case PXA_SSP_AUDIO_DIV_ACDS: - val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_AUDIO_DIV_SCDB: - val = pxa_ssp_read_reg(ssp, SSACD); - val &= ~SSACD_SCDB; - if (ssp->type == PXA3xx_SSP) - val &= ~SSACD_SCDX8; - switch (div) { - case PXA_SSP_CLK_SCDB_1: - val |= SSACD_SCDB; - break; - case PXA_SSP_CLK_SCDB_4: - break; - case PXA_SSP_CLK_SCDB_8: - if (ssp->type == PXA3xx_SSP) - val |= SSACD_SCDX8; - else - return -EINVAL; - break; - default: - return -EINVAL; - } - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_DIV_SCR: - pxa_ssp_set_scr(ssp, div); - break; - default: - return -ENODEV; - } - - return 0; -} - /* * Configure the PLL frequency pxa27x and (afaik - pxa320 only) */ -static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) +static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; if (ssp->type == PXA3xx_SSP) pxa_ssp_write_reg(ssp, SSACDD, 0); - switch (freq_out) { + switch (freq) { case 5622000: break; case 11345000: @@ -353,7 +290,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, u64 tmp = 19968; tmp *= 1000000; - do_div(tmp, freq_out); + do_div(tmp, freq); val = tmp; val = (val << 16) | 64; @@ -363,7 +300,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, dev_dbg(&ssp->pdev->dev, "Using SSACDD %x to supply %uHz\n", - val, freq_out); + val, freq); break; } @@ -568,6 +505,24 @@ static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) return 0; } +struct pxa_ssp_clock_mode { + int rate; + int pll; + u8 acds; + u8 scdb; +}; + +static const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = { + { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X }, + { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X }, + { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X }, + { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X }, + {} +}; + /* * Set the SSP audio DMA parameters and sample size. * Can be called multiple times by oss emulation. @@ -579,11 +534,12 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; int chn = params_channels(params); - u32 sscr0; - u32 sspsp; + u32 sscr0, sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; struct snd_dmaengine_dai_dma_data *dma_data; + int rate = params_rate(params); + int bclk = rate * chn * (width / 8); int ret; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -623,11 +579,57 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, } pxa_ssp_write_reg(ssp, SSCR0, sscr0); + if (sscr0 & SSCR0_ACS) { + ret = pxa_ssp_set_pll(priv, bclk); + + /* + * If we were able to generate the bclk directly, + * all is fine. Otherwise, look up the closest rate + * from the table and also set the dividers. + */ + + if (ret < 0) { + const struct pxa_ssp_clock_mode *m; + int ssacd, acds; + + for (m = pxa_ssp_clock_modes; m->rate; m++) { + if (m->rate == rate) + break; + } + + if (!m->rate) + return -EINVAL; + + acds = m->acds; + + /* The values in the table are for 16 bits */ + if (width == 32) + acds--; + + ret = pxa_ssp_set_pll(priv, bclk); + if (ret < 0) + return ret; + + ssacd = pxa_ssp_read_reg(ssp, SSACD); + ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X); + ssacd |= SSACD_ACDS(m->acds); + ssacd |= m->scdb; + pxa_ssp_write_reg(ssp, SSACD, ssacd); + } + } else if (sscr0 & SSCR0_ECS) { + /* + * For setups with external clocking, the PLL and its diviers + * are not active. Instead, the SCR bits in SSCR0 can be used + * to divide the clock. + */ + pxa_ssp_set_scr(ssp, bclk / rate); + } + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sspsp = pxa_ssp_read_reg(ssp, SSPSP); - if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { + if (((priv->sysclk / bclk) == 64) && (width == 16)) { /* This is a special case where the bitclk is 64fs * and we're not dealing with 2*32 bits of audio * samples. @@ -812,8 +814,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { .trigger = pxa_ssp_trigger, .hw_params = pxa_ssp_hw_params, .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, .set_fmt = pxa_ssp_set_dai_fmt, .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, .set_tristate = pxa_ssp_set_dai_tristate, -- cgit v1.3-6-gb490