/* * LM4857 AMP driver * * Copyright 2007 Wolfson Microelectronics PLC. * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com * Copyright 2011 Lars-Peter Clausen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include #include #include #include #include #include #include #include static const struct reg_default lm4857_default_regs[] = { { 0x0, 0x00 }, { 0x1, 0x00 }, { 0x2, 0x00 }, { 0x3, 0x00 }, }; /* The register offsets in the cache array */ #define LM4857_MVOL 0 #define LM4857_LVOL 1 #define LM4857_RVOL 2 #define LM4857_CTRL 3 /* the shifts required to set these bits */ #define LM4857_3D 5 #define LM4857_WAKEUP 5 #define LM4857_EPGAIN 4 static const unsigned int lm4857_mode_values[] = { 0, 6, 7, 8, 9, }; static const char * const lm4857_mode_texts[] = { "Off", "Earpiece", "Loudspeaker", "Loudspeaker + Headphone", "Headphone", }; static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum, LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values); static const struct snd_kcontrol_new lm4857_mode_ctrl = SOC_DAPM_ENUM("Mode", lm4857_mode_enum); static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN"), SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), SND_SOC_DAPM_OUTPUT("LS"), SND_SOC_DAPM_OUTPUT("HP"), SND_SOC_DAPM_OUTPUT("EP"), }; static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); static const struct snd_kcontrol_new lm4857_controls[] = { SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, stereo_tlv), SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, stereo_tlv), SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, mono_tlv), SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, LM4857_WAKEUP, 1, 0), SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, LM4857_EPGAIN, 1, 0), }; static const struct snd_soc_dapm_route lm4857_routes[] = { { "Mode", NULL, "IN" }, { "LS", "Loudspeaker", "Mode" }, { "LS", "Loudspeaker + Headphone", "Mode" }, { "HP", "Headphone", "Mode" }, { "HP", "Loudspeaker + Headphone", "Mode" }, { "EP", "Earpiece", "Mode" }, }; static struct snd_soc_component_driver lm4857_component_driver = { .controls = lm4857_controls, .num_controls = ARRAY_SIZE(lm4857_controls), .dapm_widgets = lm4857_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets), .dapm_routes = lm4857_routes, .num_dapm_routes = ARRAY_SIZE(lm4857_routes), }; static const struct regmap_config lm4857_regmap_config = { .val_bits = 6, .reg_bits = 2, .max_register = LM4857_CTRL, .cache_type = REGCACHE_FLAT, .reg_defaults = lm4857_default_regs, .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), }; static int lm4857_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct regmap *regmap; regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); return devm_snd_soc_register_component(&i2c->dev, &lm4857_component_driver, NULL, 0); } static const struct i2c_device_id lm4857_i2c_id[] = { { "lm4857", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", }, .probe = lm4857_i2c_probe, .id_table = lm4857_i2c_id, }; module_i2c_driver(lm4857_i2c_driver); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("LM4857 amplifier driver"); MODULE_LICENSE("GPL"); value='3' selected='selected'>3space:mode:
authorDouglas Miller <dougmill@linux.vnet.ibm.com>2017-01-28 06:42:20 -0600
committerTejun Heo <tj@kernel.org>2017-01-28 07:49:42 -0500
commit966d2b04e070bc040319aaebfec09e0144dc3341 (patch)
tree4b96156e3d1dd4dfd6039b7c219c9dc4616da52d /tools/perf/util/env.c
parent1b1bc42c1692e9b62756323c675a44cb1a1f9dbd (diff)
percpu-refcount: fix reference leak during percpu-atomic transition
percpu_ref_tryget() and percpu_ref_tryget_live() should return "true" IFF they acquire a reference. But the return value from atomic_long_inc_not_zero() is a long and may have high bits set, e.g. PERCPU_COUNT_BIAS, and the return value of the tryget routines is bool so the reference may actually be acquired but the routines return "false" which results in a reference leak since the caller assumes it does not need to do a corresponding percpu_ref_put(). This was seen when performing CPU hotplug during I/O, as hangs in blk_mq_freeze_queue_wait where percpu_ref_kill (blk_mq_freeze_queue_start) raced with percpu_ref_tryget (blk_mq_timeout_work). Sample stack trace: __switch_to+0x2c0/0x450 __schedule+0x2f8/0x970 schedule+0x48/0xc0 blk_mq_freeze_queue_wait+0x94/0x120 blk_mq_queue_reinit_work+0xb8/0x180 blk_mq_queue_reinit_prepare+0x84/0xa0 cpuhp_invoke_callback+0x17c/0x600 cpuhp_up_callbacks+0x58/0x150 _cpu_up+0xf0/0x1c0 do_cpu_up+0x120/0x150 cpu_subsys_online+0x64/0xe0 device_online+0xb4/0x120 online_store+0xb4/0xc0 dev_attr_store+0x68/0xa0 sysfs_kf_write+0x80/0xb0 kernfs_fop_write+0x17c/0x250 __vfs_write+0x6c/0x1e0 vfs_write+0xd0/0x270 SyS_write+0x6c/0x110 system_call+0x38/0xe0 Examination of the queue showed a single reference (no PERCPU_COUNT_BIAS, and __PERCPU_REF_DEAD, __PERCPU_REF_ATOMIC set) and no requests. However, conditions at the time of the race are count of PERCPU_COUNT_BIAS + 0 and __PERCPU_REF_DEAD and __PERCPU_REF_ATOMIC set. The fix is to make the tryget routines use an actual boolean internally instead of the atomic long result truncated to a int. Fixes: e625305b3907 percpu-refcount: make percpu_ref based on longs instead of ints Link: https://bugzilla.kernel.org/show_bug.cgi?id=190751 Signed-off-by: Douglas Miller <dougmill@linux.vnet.ibm.com> Reviewed-by: Jens Axboe <axboe@fb.com> Signed-off-by: Tejun Heo <tj@kernel.org> Fixes: e625305b3907 ("percpu-refcount: make percpu_ref based on longs instead of ints") Cc: stable@vger.kernel.org # v3.18+
Diffstat (limited to 'tools/perf/util/env.c')