https://source.android.com/docs/security/bulletin/2023-12-01 * tag 'ASB-2023-12-05_4.19-stable' of https://android.googlesource.com/kernel/common: Revert "macsec: use DEV_STATS_INC()" Revert "net: add DEV_STATS_READ() helper" Linux 4.19.300 net: sched: fix race condition in qdisc_graft() iomap: Set all uptodate bits for an Uptodate page scsi: virtio_scsi: limit number of hw queues by nr_cpu_ids drm/amdgpu: fix error handling in amdgpu_bo_list_get() ext4: remove gdb backup copy for meta bg in setup_new_flex_group_blocks ext4: correct return value of ext4_convert_meta_bg ext4: correct offset of gdb backup in non meta_bg group to update_backups ext4: apply umask if ACL support is disabled Revert "net: r8169: Disable multicast filter for RTL8168H and RTL8107E" media: venus: hfi: add checks to handle capabilities from firmware media: venus: hfi: fix the check to handle session buffer requirement media: venus: hfi_parser: Add check to keep the number of codecs within range media: sharp: fix sharp encoding media: lirc: drop trailing space from scancode transmit i2c: i801: fix potential race in i801_block_transaction_byte_by_byte net: dsa: lan9303: consequently nested-lock physical MDIO tty: serial: meson: fix hard LOCKUP on crtscts mode serial: meson: Use platform_get_irq() to get the interrupt tty: serial: meson: retrieve port FIFO size from DT serial: meson: remove redundant initialization of variable id tty: serial: meson: if no alias specified use an available id ALSA: hda/realtek - Enable internal speaker of ASUS K6500ZC ALSA: info: Fix potential deadlock at disconnection parisc/pgtable: Do not drop upper 5 address bits of physical address parisc: Prevent booting 64-bit kernels on PA1.x machines dmaengine: stm32-mdma: correct desc prep when channel running mcb: fix error handling for different scenarios when parsing quota: explicitly forbid quota files from being encrypted jbd2: fix potential data lost in recovering journal raced with synchronizing fs bdev PM: hibernate: Clean up sync_read handling in snapshot_write_next() PM: hibernate: Use __get_safe_page() rather than touching the list mmc: vub300: fix an error code clk: qcom: ipq8074: drop the CLK_SET_RATE_PARENT flag from PLL clocks parisc/pdc: Add width field to struct pdc_model PCI: keystone: Don't discard .probe() callback PCI: keystone: Don't discard .remove() callback genirq/generic_chip: Make irq_remove_generic_chip() irqdomain aware mmc: meson-gx: Remove setting of CMD_CFG_ERROR PCI/sysfs: Protect driver's D3cold preference from user space hvc/xen: fix error path in xen_hvc_init() to always register frontend driver audit: don't WARN_ON_ONCE(!current->mm) in audit_exe_compare() audit: don't take task_lock() in audit_exe_compare() code path KVM: x86: Ignore MSR_AMD64_TW_CFG access randstruct: Fix gcc-plugin performance mode to stay in group media: venus: hfi: add checks to perform sanity on queue pointers cifs: spnego: add ';' in HOST_KEY_LEN macvlan: Don't propagate promisc change to lower dev in passthru net: ethernet: cortina: Fix MTU max setting net: ethernet: cortina: Handle large frames net: ethernet: cortina: Fix max RX frame define ptp: annotate data-race around q->head and q->tail xen/events: fix delayed eoi list handling ppp: limit MRU to 64K tipc: Fix kernel-infoleak due to uninitialized TLV value tty: Fix uninit-value access in ppp_sync_receive() ipvlan: add ipvlan_route_v6_outbound() helper NFSv4.1: fix SP4_MACH_CRED protection for pnfs IO pwm: Fix double shift bug drm/amd/display: Avoid NULL dereference of timing generator gfs2: ignore negated quota changes media: vivid: avoid integer overflow media: gspca: cpia1: shift-out-of-bounds in set_flicker i2c: sun6i-p2wi: Prevent potential division by zero usb: gadget: f_ncm: Always set current gadget in ncm_bind() tty: vcc: Add check for kstrdup() in vcc_probe() HID: Add quirk for Dell Pro Wireless Keyboard and Mouse KM5221W scsi: libfc: Fix potential NULL pointer dereference in fc_lport_ptp_setup() atm: iphase: Do PCI error checks on own line ALSA: hda: Fix possible null-ptr-deref when assigning a stream ARM: 9320/1: fix stack depot IRQ stack filter jfs: fix array-index-out-of-bounds in diAlloc jfs: fix array-index-out-of-bounds in dbFindLeaf fs/jfs: Add validity check for db_maxag and db_agpref fs/jfs: Add check for negative db_l2nbperpage RDMA/hfi1: Use FIELD_GET() to extract Link Width crypto: pcrypt - Fix hungtask for PADATA_RESET selftests/efivarfs: create-read: fix a resource leak drm/amdgpu: Fix a null pointer access when the smc_rreg pointer is NULL drm/amd: Fix UBSAN array-index-out-of-bounds for Polaris and Tonga drm/amd: Fix UBSAN array-index-out-of-bounds for SMU7 platform/x86: thinkpad_acpi: Add battery quirk for Thinkpad X120e Bluetooth: Fix double free in hci_conn_cleanup net: annotate data-races around sk->sk_dst_pending_confirm net: annotate data-races around sk->sk_tx_queue_mapping wifi: ath10k: fix clang-specific fortify warning wifi: ath9k: fix clang-specific fortify warnings wifi: mac80211: don't return unset power in ieee80211_get_tx_power() x86/mm: Drop the 4 MB restriction on minimal NUMA node memory size clocksource/drivers/timer-atmel-tcb: Fix initialization on SAM9 hardware clocksource/drivers/timer-imx-gpt: Fix potential memory leak perf/core: Bail out early if the request AUX area is out of bound locking/ww_mutex/test: Fix potential workqueue corruption Revert "ipvlan: properly track tx_errors" ANDROID: fix up platform_device ABI break Linux 4.19.299 btrfs: use u64 for buffer sizes in the tree search ioctls Revert "mmc: core: Capture correct oemid-bits for eMMC cards" fbdev: fsl-diu-fb: mark wr_reg_wa() static fbdev: imsttfb: fix a resource leak in probe fbdev: imsttfb: Fix error path of imsttfb_probe() netfilter: xt_recent: fix (increase) ipv6 literal buffer length r8169: respect userspace disabling IFF_MULTICAST tg3: power down device only on SYSTEM_POWER_OFF net/smc: fix dangling sock under state SMC_APPFINCLOSEWAIT net/smc: wait for pending work before clcsock release_sock net/smc: postpone release of clcsock net: r8169: Disable multicast filter for RTL8168H and RTL8107E r8169: improve rtl_set_rx_mode dccp/tcp: Call security_inet_conn_request() after setting IPv6 addresses. dccp: Call security_inet_conn_request() after setting IPv4 addresses. tipc: Change nla_policy for bearer-related names to NLA_NUL_STRING llc: verify mac len before reading mac header Input: synaptics-rmi4 - fix use after free in rmi_unregister_function() pwm: brcmstb: Utilize appropriate clock APIs in suspend/resume pwm: sti: Reduce number of allocations and drop usage of chip_data pwm: sti: Avoid conditional gotos media: dvb-usb-v2: af9035: fix missing unlock media: s3c-camif: Avoid inappropriate kfree() media: bttv: fix use after free error due to btv->timeout timer pcmcia: ds: fix possible name leak in error path in pcmcia_device_add() pcmcia: ds: fix refcount leak in pcmcia_device_add() pcmcia: cs: fix possible hung task and memory leak pccardd() f2fs: fix to initialize map.m_pblk in f2fs_precache_extents() dmaengine: pxa_dma: Remove an erroneous BUG_ON() in pxad_free_desc() USB: usbip: fix stub_dev hub disconnect tools: iio: iio_generic_buffer ensure alignment tools: iio: iio_generic_buffer: Fix some integer type and calculation tools: iio: privatize globals and functions in iio_generic_buffer.c file misc: st_core: Do not call kfree_skb() under spin_lock_irqsave() dmaengine: ti: edma: handle irq_of_parse_and_map() errors usb: dwc2: fix possible NULL pointer dereference caused by driver concurrency tty: tty_jobctrl: fix pid memleak in disassociate_ctty() leds: trigger: ledtrig-cpu:: Fix 'output may be truncated' issue for 'cpu' ledtrig-cpu: Limit to 8 CPUs leds: pwm: Don't disable the PWM when the LED should be off leds: pwm: convert to atomic PWM API leds: pwm: simplify if condition mfd: dln2: Fix double put in dln2_probe ASoC: ams-delta.c: use component after check ASoC: Intel: Skylake: Fix mem leak when parsing UUIDs fails sh: bios: Revive earlyprintk support RDMA/hfi1: Workaround truncation compilation error ext4: move 'ix' sanity check to corrent position ARM: 9321/1: memset: cast the constant byte to unsigned char hid: cp2112: Fix duplicate workqueue initialization HID: cp2112: Use irqchip template nd_btt: Make BTT lanes preemptible sched/rt: Provide migrate_disable/enable() inlines hwrng: geode - fix accessing registers clk: scmi: Free scmi_clk allocated when the clocks with invalid info are skipped firmware: ti_sci: Mark driver as non removable ARM: dts: qcom: mdm9615: populate vsdcc fixed regulator drm/rockchip: cdn-dp: Fix some error handling paths in cdn_dp_probe() drm/radeon: possible buffer overflow drm/rockchip: vop: Fix reset of state in duplicate state crtc funcs hwmon: (coretemp) Fix potentially truncated sysfs attribute name platform/x86: wmi: Fix opening of char device platform/x86: wmi: remove unnecessary initializations platform/x86: wmi: Fix probe failure when failing to register WMI devices clk: mediatek: clk-mt2701: Add check for mtk_alloc_clk_data clk: mediatek: clk-mt6797: Add check for mtk_alloc_clk_data clk: npcm7xx: Fix incorrect kfree clk: keystone: pll: fix a couple NULL vs IS_ERR() checks clk: qcom: clk-rcg2: Fix clock rate overflow for high parent frequencies regmap: debugfs: Fix a erroneous check after snprintf() ipvlan: properly track tx_errors net: add DEV_STATS_READ() helper macsec: use DEV_STATS_INC() macsec: Fix traffic counters/statistics ipv6: avoid atomic fragment on GSO packets ACPI: sysfs: Fix create_pnp_modalias() and create_of_modalias() chtls: fix tp->rcv_tstamp initialization thermal: core: prevent potential string overflow can: dev: can_restart(): fix race condition between controller restart and netif_carrier_on() can: dev: can_restart(): don't crash kernel if carrier is OK can: dev: move driver related infrastructure into separate subdir wifi: rtlwifi: fix EDCA limit set by BT coexistence tcp_metrics: do not create an entry from tcp_init_metrics() tcp_metrics: properly set tp->snd_ssthresh in tcp_init_metrics() tcp_metrics: add missing barriers on delete i40e: fix potential memory leaks in i40e_remove() genirq/matrix: Exclude managed interrupts in irq_matrix_allocated() vfs: fix readahead(2) on block devices Linux 4.19.298 tty: 8250: Add support for Intashield IS-100 tty: 8250: Add support for Brainboxes UP cards tty: 8250: Add support for additional Brainboxes UC cards tty: 8250: Remove UC-257 and UC-431 usb: storage: set 1.50 as the lower bcdDevice for older "Super Top" compatibility PCI: Prevent xHCI driver from claiming AMD VanGogh USB3 DRD device remove the sx8 block driver ata: ahci: fix enum constants for gcc-13 net: chelsio: cxgb4: add an error code check in t4_load_phy_fw platform/x86: asus-wmi: Change ASUS_WMI_BRN_DOWN code from 0x20 to 0x2e scsi: mpt3sas: Fix in error path fbdev: uvesafb: Call cn_del_callback() at the end of uvesafb_exit() ASoC: rt5650: fix the wrong result of key button netfilter: nfnetlink_log: silence bogus compiler warning fbdev: atyfb: only use ioremap_uc() on i386 and ia64 Input: synaptics-rmi4 - handle reset delay when using SMBus trsnsport dmaengine: ste_dma40: Fix PM disable depth imbalance in d40_probe irqchip/stm32-exti: add missing DT IRQ flag translation Input: i8042 - add Fujitsu Lifebook E5411 to i8042 quirk table ASoC: simple-card: fixup asoc_simple_probe() error handling MAINTAINERS: r8169: Update path to the driver x86: Fix .brk attribute in linker script rpmsg: Fix possible refcount leak in rpmsg_register_device_override() rpmsg: glink: Release driver_override rpmsg: Fix calling device_lock() on non-initialized device rpmsg: Fix kfree() of static memory on setting driver_override rpmsg: Constify local variable in field store macro driver: platform: Add helper for safer setting of driver_override x86/mm: Fix RESERVE_BRK() for older binutils x86/mm: Simplify RESERVE_BRK() nfsd: lock_rename() needs both directories to live on the same fs f2fs: fix to do sanity check on inode type during garbage collection smbdirect: missing rc checks while waiting for rdma events kobject: Fix slab-out-of-bounds in fill_kobj_path() arm64: fix a concurrency issue in emulation_proc_handler() drm/dp_mst: Fix NULL deref in get_mst_branch_device_by_guid_helper() ARM: 8933/1: replace Sun/Solaris style flag on section directive NFS: Don't call generic_error_remove_page() while holding locks x86/i8259: Skip probing when ACPI/MADT advertises PCAT compatibility iio: exynos-adc: request second interupt only when touchscreen mode is used perf/core: Fix potential NULL deref nvmem: imx: correct nregs for i.MX6UL nvmem: imx: correct nregs for i.MX6SLL i2c: stm32f7: Fix PEC handling in case of SMBUS transfers i2c: muxes: i2c-demux-pinctrl: Use of_get_i2c_adapter_by_node() i2c: muxes: i2c-mux-gpmux: Use of_get_i2c_adapter_by_node() i2c: muxes: i2c-mux-pinctrl: Use of_get_i2c_adapter_by_node() i40e: Fix wrong check for I40E_TXR_FLAGS_WB_ON_ITR gtp: fix fragmentation needed check with gso igb: Fix potential memory leak in igb_add_ethtool_nfc_entry treewide: Spelling fix in comment r8169: fix the KCSAN reported data race in rtl_rx while reading desc->opts1 r8169: fix the KCSAN reported data-race in rtl_tx while reading TxDescArray[entry].opts1 r8169: rename r8169.c to r8169_main.c virtio-mmio: fix memory leak of vm_dev virtio_balloon: Fix endless deflation and inflation on arm64 mcb-lpc: Reallocate memory region to avoid memory overlapping mcb: Return actual parsed size when reading chameleon table selftests/ftrace: Add new test case which checks non unique symbol mmc: core: sdio: hold retuning if sdio in 1-bit mode mmc: sdio: Don't re-initialize powered-on removable SDIO cards at resume Conflicts: drivers/clk/qcom/clk-rcg2.c drivers/leds/leds-pwm.c drivers/mmc/core/sdio.c drivers/rpmsg/qcom_glink_native.c drivers/thermal/thermal_core.c drivers/usb/gadget/function/f_ncm.c Change-Id: I230a2c820e39dd863a874bfc0c7a411896b0ba9c
334 lines
7.9 KiB
C
334 lines
7.9 KiB
C
/*
|
|
* linux/drivers/leds-pwm.c
|
|
*
|
|
* simple PWM based LED control
|
|
*
|
|
* Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
|
|
*
|
|
* based on leds-gpio.c by Raphael Assenat <raph@8d.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/err.h>
|
|
#include <linux/pwm.h>
|
|
#include <linux/leds_pwm.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define PWM_PERIOD_DEFAULT_NS 1000000
|
|
|
|
struct pwm_setting {
|
|
u64 period_ns;
|
|
u64 duty_ns;
|
|
};
|
|
|
|
struct led_setting {
|
|
u64 on_ms;
|
|
u64 off_ms;
|
|
enum led_brightness brightness;
|
|
bool blink;
|
|
};
|
|
|
|
struct led_pwm_data {
|
|
struct led_classdev cdev;
|
|
struct pwm_device *pwm;
|
|
struct pwm_setting pwm_setting;
|
|
struct led_setting led_setting;
|
|
struct pwm_state pwmstate;
|
|
unsigned int active_low;
|
|
bool blinking;
|
|
};
|
|
|
|
struct led_pwm_priv {
|
|
int num_leds;
|
|
struct led_pwm_data leds[0];
|
|
};
|
|
|
|
static int __led_blink_config_pwm(struct led_pwm_data *led_data)
|
|
{
|
|
struct pwm_state pstate;
|
|
int rc;
|
|
|
|
pwm_get_state(led_data->pwm, &pstate);
|
|
pstate.enabled = !!(led_data->pwm_setting.duty_ns != 0);
|
|
pstate.period = led_data->pwm_setting.period_ns;
|
|
pstate.duty_cycle = led_data->pwm_setting.duty_ns;
|
|
/* Use default pattern in PWM device */
|
|
pstate.output_pattern = NULL;
|
|
|
|
rc = pwm_apply_state(led_data->pwm, &pstate);
|
|
if (rc < 0)
|
|
pr_err("Apply PWM state for %s led failed, rc=%d\n",
|
|
led_data->cdev.name, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int led_pwm_set_blink(struct led_pwm_data *led_data)
|
|
{
|
|
u64 on_ms, off_ms, period_ns, duty_ns;
|
|
enum led_brightness brightness = led_data->led_setting.brightness;
|
|
int rc = 0;
|
|
|
|
if (led_data->led_setting.blink) {
|
|
on_ms = led_data->led_setting.on_ms;
|
|
off_ms = led_data->led_setting.off_ms;
|
|
|
|
duty_ns = on_ms * NSEC_PER_MSEC;
|
|
period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
|
|
|
|
if (period_ns < duty_ns && duty_ns != 0)
|
|
period_ns = duty_ns + 1;
|
|
|
|
} else {
|
|
/* Use initial period if no blinking is required */
|
|
period_ns = PWM_PERIOD_DEFAULT_NS;
|
|
|
|
duty_ns = period_ns * brightness;
|
|
do_div(duty_ns, LED_FULL);
|
|
|
|
if (period_ns < duty_ns && duty_ns != 0)
|
|
period_ns = duty_ns + 1;
|
|
}
|
|
|
|
pr_debug("BLINK: PWM settings for %s led: period = %lluns, duty = %lluns brightness = %d\n",
|
|
led_data->cdev.name, period_ns, duty_ns, brightness);
|
|
|
|
led_data->pwm_setting.duty_ns = duty_ns;
|
|
led_data->pwm_setting.period_ns = period_ns;
|
|
|
|
rc = __led_blink_config_pwm(led_data);
|
|
if (rc < 0) {
|
|
pr_err("failed to config pwm for blink %s failed, rc=%d\n",
|
|
led_data->cdev.name, rc);
|
|
return rc;
|
|
}
|
|
|
|
if (led_data->led_setting.blink) {
|
|
led_data->cdev.brightness = LED_FULL;
|
|
led_data->blinking = true;
|
|
} else {
|
|
led_data->cdev.brightness = led_data->led_setting.brightness;
|
|
led_data->blinking = false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int led_pwm_blink_set(struct led_classdev *led_cdev,
|
|
unsigned long *on_ms, unsigned long *off_ms)
|
|
{
|
|
struct led_pwm_data *led_data =
|
|
container_of(led_cdev, struct led_pwm_data, cdev);
|
|
int rc = 0;
|
|
|
|
if (led_data->blinking && *on_ms == led_data->led_setting.on_ms &&
|
|
*off_ms == led_data->led_setting.off_ms) {
|
|
pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n",
|
|
*on_ms, *off_ms);
|
|
return 0;
|
|
}
|
|
|
|
if (*on_ms == 0) {
|
|
led_data->led_setting.blink = false;
|
|
led_data->led_setting.brightness = LED_OFF;
|
|
} else if (*off_ms == 0) {
|
|
led_data->led_setting.blink = false;
|
|
led_data->led_setting.brightness = led_data->cdev.brightness;
|
|
} else {
|
|
led_data->led_setting.on_ms = *on_ms;
|
|
led_data->led_setting.off_ms = *off_ms;
|
|
led_data->led_setting.blink = true;
|
|
}
|
|
|
|
rc = led_pwm_set_blink(led_data);
|
|
if (rc < 0)
|
|
pr_err("blink led failed for rc=%d\n", rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int led_pwm_set(struct led_classdev *led_cdev,
|
|
enum led_brightness brightness)
|
|
{
|
|
struct led_pwm_data *led_data =
|
|
container_of(led_cdev, struct led_pwm_data, cdev);
|
|
unsigned int max = led_data->cdev.max_brightness;
|
|
unsigned long long duty = led_data->pwmstate.period;
|
|
|
|
duty *= brightness;
|
|
do_div(duty, max);
|
|
|
|
if (led_data->active_low)
|
|
duty = led_data->pwmstate.period - duty;
|
|
|
|
led_data->pwmstate.duty_cycle = duty;
|
|
led_data->pwmstate.enabled = true;
|
|
return pwm_apply_state(led_data->pwm, &led_data->pwmstate);
|
|
}
|
|
|
|
static inline size_t sizeof_pwm_leds_priv(int num_leds)
|
|
{
|
|
return sizeof(struct led_pwm_priv) +
|
|
(sizeof(struct led_pwm_data) * num_leds);
|
|
}
|
|
|
|
static void led_pwm_cleanup(struct led_pwm_priv *priv)
|
|
{
|
|
while (priv->num_leds--)
|
|
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
|
|
}
|
|
|
|
static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
|
|
struct led_pwm *led, struct device_node *child)
|
|
{
|
|
struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
|
|
int ret;
|
|
|
|
led_data->active_low = led->active_low;
|
|
led_data->cdev.name = led->name;
|
|
led_data->cdev.default_trigger = led->default_trigger;
|
|
led_data->cdev.brightness = LED_OFF;
|
|
led_data->cdev.max_brightness = led->max_brightness;
|
|
/* Set a flag to keep the trigger always */
|
|
led_data->cdev.flags |= LED_KEEP_TRIGGER;
|
|
|
|
if (child)
|
|
led_data->pwm = devm_of_pwm_get(dev, child, NULL);
|
|
else
|
|
led_data->pwm = devm_pwm_get(dev, led->name);
|
|
if (IS_ERR(led_data->pwm)) {
|
|
ret = PTR_ERR(led_data->pwm);
|
|
if (ret != -EPROBE_DEFER)
|
|
dev_err(dev, "unable to request PWM for %s: %d\n",
|
|
led->name, ret);
|
|
return ret;
|
|
}
|
|
|
|
led_data->cdev.brightness_set_blocking = led_pwm_set;
|
|
led_data->cdev.blink_set = led_pwm_blink_set;
|
|
|
|
pwm_init_state(led_data->pwm, &led_data->pwmstate);
|
|
|
|
if (!led_data->pwmstate.period)
|
|
led_data->pwmstate.period = led->pwm_period_ns;
|
|
|
|
ret = led_classdev_register(dev, &led_data->cdev);
|
|
if (ret == 0) {
|
|
priv->num_leds++;
|
|
led_pwm_set(&led_data->cdev, led_data->cdev.brightness);
|
|
} else {
|
|
dev_err(dev, "failed to register PWM led for %s: %d\n",
|
|
led->name, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
|
|
{
|
|
struct device_node *child;
|
|
struct led_pwm led;
|
|
int ret = 0;
|
|
|
|
memset(&led, 0, sizeof(led));
|
|
|
|
for_each_child_of_node(dev->of_node, child) {
|
|
led.name = of_get_property(child, "label", NULL) ? :
|
|
child->name;
|
|
|
|
led.default_trigger = of_get_property(child,
|
|
"linux,default-trigger", NULL);
|
|
led.active_low = of_property_read_bool(child, "active-low");
|
|
of_property_read_u32(child, "max-brightness",
|
|
&led.max_brightness);
|
|
|
|
ret = led_pwm_add(dev, priv, &led, child);
|
|
if (ret) {
|
|
of_node_put(child);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int led_pwm_probe(struct platform_device *pdev)
|
|
{
|
|
struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct led_pwm_priv *priv;
|
|
int count, i;
|
|
int ret = 0;
|
|
|
|
if (pdata)
|
|
count = pdata->num_leds;
|
|
else
|
|
count = of_get_child_count(pdev->dev.of_node);
|
|
|
|
if (!count)
|
|
return -EINVAL;
|
|
|
|
priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count),
|
|
GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
if (pdata) {
|
|
for (i = 0; i < count; i++) {
|
|
ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
|
|
NULL);
|
|
if (ret)
|
|
break;
|
|
}
|
|
} else {
|
|
ret = led_pwm_create_of(&pdev->dev, priv);
|
|
}
|
|
|
|
if (ret) {
|
|
led_pwm_cleanup(priv);
|
|
return ret;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int led_pwm_remove(struct platform_device *pdev)
|
|
{
|
|
struct led_pwm_priv *priv = platform_get_drvdata(pdev);
|
|
|
|
led_pwm_cleanup(priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id of_pwm_leds_match[] = {
|
|
{ .compatible = "pwm-leds", },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
|
|
|
|
static struct platform_driver led_pwm_driver = {
|
|
.probe = led_pwm_probe,
|
|
.remove = led_pwm_remove,
|
|
.driver = {
|
|
.name = "leds_pwm",
|
|
.of_match_table = of_pwm_leds_match,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(led_pwm_driver);
|
|
|
|
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
|
|
MODULE_DESCRIPTION("generic PWM LED driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:leds-pwm");
|