aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/processor_perflib.c
AgeCommit message (Expand)AuthorFilesLines
2018-06-12treewide: kmalloc() -> kmalloc_array()Kees Cook1-2/+3
2018-03-21xen/acpi: upload _PSD info for non Dom0 CPUs tooJoao Martins1-6/+5
2018-02-04ACPI: processor_perflib: Do not send _PPC change notification if not readyChen Yu1-1/+1
2017-02-04ACPI: processor_perflib: Simplify code and stop using CPUFREQ_STARTViresh Kumar1-3/+1
2016-11-21ACPI / processor: Make acpi_processor_ppc_has_changed() voidRafael J. Wysocki1-6/+4
2016-11-17cpufreq: intel_pstate: Request P-states control from SMM if neededRafael J. Wysocki1-17/+28
2015-09-01Merge branch 'pm-cpufreq'Rafael J. Wysocki1-4/+2
2015-09-01cpufreq: remove redundant CPUFREQ_INCOMPATIBLE notifier eventViresh Kumar1-1/+1
2015-07-22ACPI / processor: Drop an unused argument of a cleanup routineRafael J. Wysocki1-3/+1
2015-07-08ACPI: Remove FSF mailing addressesJarkko Nikula1-4/+0
2014-02-21ACPI / processor: use acpi_evaluate_ost() to replace open-coded versionJiang Liu1-11/+3
2013-12-07ACPI: Clean up inclusions of ACPI header filesLv Zheng1-5/+2
2013-10-30ACPI / processor: Do not request ACPI cpufreq module directlyRafael J. Wysocki1-22/+0
2013-07-15ACPI: introduce helper function acpi_has_method()Jiang Liu1-15/+7
2013-06-25ACPI / processor: Drop unused variable from processor_perflib.cLan Tianyu1-3/+1
2013-03-06acpi: Export the acpi_processor_get_performance_infoKonrad Rzeszutek Wilk1-2/+2
2013-01-22ACPI: Check MSR valid bit before using P-state frequenciesStefan Bader1-0/+7
2012-09-09ACPI: Add fixups for AMD P-state figuresMatthew Garrett1-0/+30
2012-05-08ACPI: Ignore invalid _PSS entries, but use valid onesMarco Aurelio da Costa1-5/+25
2012-01-26ACPI: Load acpi-cpufreq from processor driver automaticallyAndi Kleen1-0/+22
2011-05-04[CPUFREQ] use dynamic debug instead of custom infrastructureDominik Brodowski1-5/+1
2010-09-28ACPI: Fix typosLucas De Marchi1-2/+2
2010-03-30include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.hTejun Heo1-0/+1
2010-03-03Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpuLinus Torvalds1-1/+1
2010-02-19ACPI: Fix regression where _PPC is not read at boot even when ignore_ppc=0Darrick J. Wong1-1/+5
2010-02-17percpu: add __percpu sparse annotations to what's leftTejun Heo1-1/+1
2009-12-16Merge branch 'ost' into releaseLen Brown1-3/+47
2009-11-24[ACPI/CPUFREQ] Introduce bios_limit per cpu cpufreq sysfs interfaceThomas Renninger1-0/+13
2009-11-06ACPI: Notify the _PPC evaluation status to the platformZhao Yakui1-3/+47
2009-09-24cpumask: use zalloc_cpumask_var() where possibleLi Zefan1-2/+1
2009-08-28ACPI: Move definition of PREFIX from acpi_bus.h to internal..hLen Brown1-0/+2
2009-05-29ACPI: sanity check _PSS frequency to prevent cpufreq crashLen Brown1-3/+9
2009-04-05Merge branch 'linus' into releaseLen Brown1-2/+2
2009-03-27ACPI: Avoid wiping out pr->performance during preregisteringStanislaw Gruszka1-25/+21
2009-02-20alloc_percpu: change percpu_ptr to per_cpu_ptrRusty Russell1-2/+2
2009-02-04ACPI: cpufreq: Remove deprecated /proc/acpi/processor/../performance proc entriesThomas Renninger1-105/+0
2009-01-03cpumask: convert shared_cpu_map in acpi_processor* structs to cpumask_var_tRusty Russell1-12/+16
2008-11-07ACPI: consolidate ACPI_*_COMPONENT definitions in acpi_drivers.hBjorn Helgaas1-1/+1
2008-10-25ACPI: cpufreq, processor: fix compile error in drivers/acpi/processor_perflib.cMiao Xie1-0/+5
2008-10-22Merge branch 'ull' into testLen Brown1-1/+1
2008-10-22Merge branch 'acpica' into testLen Brown1-5/+5
2008-10-22ACPI: replace ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ...) with printkLin Ming1-5/+5
2008-10-11ACPI: Change acpi_evaluate_integer to support 64-bit on 32-bit kernelsMatthew Wilcox1-1/+1
2008-09-22ACPI: cpufreq, processor: Detect old BIOS, not supporting CPU freq on a recent CPU.Thomas Renninger1-3/+15
2008-08-18ACPI: Fix now signed module parameter.Milan Broz1-1/+1
2008-08-15ACPI: Fix thermal shutdownsMilan Broz1-1/+1
2008-07-30acpi cpufreq cleanup: move bailing out of function before locking the mutexThomas Renninger1-3/+3
2008-07-30cpufreq acpi: only call _PPC after cpufreq ACPI init funcs got called alreadyThomas Renninger1-2/+13
2008-07-16ACPI: change processors from array to per_cpu variableMike Travis1-9/+9
2008-04-29acpi: use non-racy method for proc entries creationDenis V. Lunev1-9/+4
pan> 1; } regmap_write(map, offset, val); offset += regmap_get_reg_stride(map); } /* * If we still have channel left at the end of the process, it means * the stream has more channels than we can accommodate and we should * have caught this earlier. */ if (WARN_ON(ch != 0)) { pr_err("channel mask error\n"); return -EINVAL; } return 0; } EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) { struct axg_tdm_stream *ts = formatter->stream; bool invert; int ret; /* Do nothing if the formatter is already enabled */ if (formatter->enabled) return 0; /* * On the g12a (and possibly other SoCs), when a stream using * multiple lanes is restarted, it will sometimes not start * from the first lane, but randomly from another used one. * The result is an unexpected and random channel shift. * * The hypothesis is that an HW counter is not properly reset * and the formatter simply starts on the lane it stopped * before. Unfortunately, there does not seems to be a way to * reset this through the registers of the block. * * However, the g12a has indenpendent reset lines for each audio * devices. Using this reset before each start solves the issue. */ ret = reset_control_reset(formatter->reset); if (ret) return ret; /* * If sclk is inverted, it means the bit should latched on the * rising edge which is what our HW expects. If not, we need to * invert it before the formatter. */ invert = axg_tdm_sclk_invert(ts->iface->fmt); ret = clk_set_phase(formatter->sclk, invert ? 0 : 180); if (ret) return ret; /* Setup the stream parameter in the formatter */ ret = formatter->drv->ops->prepare(formatter->map, formatter->drv->quirks, formatter->stream); if (ret) return ret; /* Enable the signal clocks feeding the formatter */ ret = clk_prepare_enable(formatter->sclk); if (ret) return ret; ret = clk_prepare_enable(formatter->lrclk); if (ret) { clk_disable_unprepare(formatter->sclk); return ret; } /* Finally, actually enable the formatter */ formatter->drv->ops->enable(formatter->map); formatter->enabled = true; return 0; } static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) { /* Do nothing if the formatter is already disabled */ if (!formatter->enabled) return; formatter->drv->ops->disable(formatter->map); clk_disable_unprepare(formatter->lrclk); clk_disable_unprepare(formatter->sclk); formatter->enabled = false; } static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) { struct axg_tdm_stream *ts = formatter->stream; int ret = 0; mutex_lock(&ts->lock); /* Catch up if the stream is already running when we attach */ if (ts->ready) { ret = axg_tdm_formatter_enable(formatter); if (ret) { pr_err("failed to enable formatter\n"); goto out; } } list_add_tail(&formatter->list, &ts->formatter_list); out: mutex_unlock(&ts->lock); return ret; } static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) { struct axg_tdm_stream *ts = formatter->stream; mutex_lock(&ts->lock); list_del(&formatter->list); mutex_unlock(&ts->lock); axg_tdm_formatter_disable(formatter); } static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, struct snd_soc_dapm_widget *w) { struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); int ret; /* * If we don't get a stream at this stage, it would mean that the * widget is powering up but is not attached to any backend DAI. * It should not happen, ever ! */ if (WARN_ON(!ts)) return -ENODEV; /* Clock our device */ ret = clk_prepare_enable(formatter->pclk); if (ret) return ret; /* Reparent the bit clock to the TDM interface */ ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); if (ret) goto disable_pclk; /* Reparent the sample clock to the TDM interface */ ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); if (ret) goto disable_pclk; formatter->stream = ts; ret = axg_tdm_formatter_attach(formatter); if (ret) goto disable_pclk; return 0; disable_pclk: clk_disable_unprepare(formatter->pclk); return ret; } static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) { axg_tdm_formatter_dettach(formatter); clk_disable_unprepare(formatter->pclk); formatter->stream = NULL; } int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event) { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); int ret = 0; switch (event) { case SND_SOC_DAPM_PRE_PMU: ret = axg_tdm_formatter_power_up(formatter, w); break; case SND_SOC_DAPM_PRE_PMD: axg_tdm_formatter_power_down(formatter); break; default: dev_err(c->dev, "Unexpected event %d\n", event); return -EINVAL; } return ret; } EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); int axg_tdm_formatter_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct axg_tdm_formatter_driver *drv; struct axg_tdm_formatter *formatter; void __iomem *regs; int ret; drv = of_device_get_match_data(dev); if (!drv) { dev_err(dev, "failed to match device\n"); return -ENODEV; } formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); if (!formatter) return -ENOMEM; platform_set_drvdata(pdev, formatter); formatter->drv = drv; regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); if (IS_ERR(formatter->map)) { dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(formatter->map)); return PTR_ERR(formatter->map); } /* Peripharal clock */ formatter->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(formatter->pclk)) { ret = PTR_ERR(formatter->pclk); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get pclk: %d\n", ret); return ret; } /* Formatter bit clock */ formatter->sclk = devm_clk_get(dev, "sclk"); if (IS_ERR(formatter->sclk)) { ret = PTR_ERR(formatter->sclk); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get sclk: %d\n", ret); return ret; } /* Formatter sample clock */ formatter->lrclk = devm_clk_get(dev, "lrclk"); if (IS_ERR(formatter->lrclk)) { ret = PTR_ERR(formatter->lrclk); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get lrclk: %d\n", ret); return ret; } /* Formatter bit clock input multiplexer */ formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); if (IS_ERR(formatter->sclk_sel)) { ret = PTR_ERR(formatter->sclk_sel); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get sclk_sel: %d\n", ret); return ret; } /* Formatter sample clock input multiplexer */ formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); if (IS_ERR(formatter->lrclk_sel)) { ret = PTR_ERR(formatter->lrclk_sel); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get lrclk_sel: %d\n", ret); return ret; } /* Formatter dedicated reset line */ formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(formatter->reset)) { ret = PTR_ERR(formatter->reset); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get reset: %d\n", ret); return ret; } return devm_snd_soc_register_component(dev, drv->component_drv, NULL, 0); } EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); int axg_tdm_stream_start(struct axg_tdm_stream *ts) { struct axg_tdm_formatter *formatter; int ret = 0; mutex_lock(&ts->lock); ts->ready = true; /* Start all the formatters attached to the stream */ list_for_each_entry(formatter, &ts->formatter_list, list) { ret = axg_tdm_formatter_enable(formatter); if (ret) { pr_err("failed to start tdm stream\n"); goto out; } } out: mutex_unlock(&ts->lock); return ret; } EXPORT_SYMBOL_GPL(axg_tdm_stream_start); void axg_tdm_stream_stop(struct axg_tdm_stream *ts) { struct axg_tdm_formatter *formatter; mutex_lock(&ts->lock); ts->ready = false; /* Stop all the formatters attached to the stream */ list_for_each_entry(formatter, &ts->formatter_list, list) { axg_tdm_formatter_disable(formatter); } mutex_unlock(&ts->lock); } EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) { struct axg_tdm_stream *ts; ts = kzalloc(sizeof(*ts), GFP_KERNEL); if (ts) { INIT_LIST_HEAD(&ts->formatter_list); mutex_init(&ts->lock); ts->iface = iface; } return ts; } EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); void axg_tdm_stream_free(struct axg_tdm_stream *ts) { /* * If the list is not empty, it would mean that one of the formatter * widget is still powered and attached to the interface while we * are removing the TDM DAI. It should not be possible */ WARN_ON(!list_empty(&ts->formatter_list)); mutex_destroy(&ts->lock); kfree(ts); } EXPORT_SYMBOL_GPL(axg_tdm_stream_free); MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); MODULE_LICENSE("GPL v2");