brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 4ed3e73 Raw
380 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * lp5523.c - LP5523, LP55231 LED Driver4 *5 * Copyright (C) 2010 Nokia Corporation6 * Copyright (C) 2012 Texas Instruments7 *8 * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>9 *          Milo(Woogyom) Kim <milo.kim@ti.com>10 */11 12#include <linux/cleanup.h>13#include <linux/delay.h>14#include <linux/firmware.h>15#include <linux/i2c.h>16#include <linux/leds.h>17#include <linux/module.h>18#include <linux/mutex.h>19#include <linux/of.h>20#include <linux/platform_data/leds-lp55xx.h>21#include <linux/slab.h>22 23#include "leds-lp55xx-common.h"24 25/* Memory is used like this:26 * 0x00 engine 1 program27 * 0x10 engine 2 program28 * 0x20 engine 3 program29 * 0x30 engine 1 muxing info30 * 0x40 engine 2 muxing info31 * 0x50 engine 3 muxing info32 */33#define LP5523_PAGES_PER_ENGINE		134#define LP5523_MAX_LEDS			935 36/* Registers */37#define LP5523_REG_ENABLE		0x0038#define LP5523_REG_OP_MODE		0x0139#define LP5523_REG_ENABLE_LEDS_MSB	0x0440#define LP5523_REG_ENABLE_LEDS_LSB	0x0541#define LP5523_REG_LED_CTRL_BASE	0x0642#define LP5523_REG_LED_PWM_BASE		0x1643#define LP5523_REG_LED_CURRENT_BASE	0x2644#define LP5523_REG_CONFIG		0x3645 46#define LP5523_REG_STATUS		0x3A47#define LP5523_ENGINE_BUSY		BIT(4)48 49#define LP5523_REG_RESET		0x3D50#define LP5523_REG_LED_TEST_CTRL	0x4151#define LP5523_REG_LED_TEST_ADC		0x4252#define LP5523_REG_MASTER_FADER_BASE	0x4853#define LP5523_REG_CH1_PROG_START	0x4C54#define LP5523_REG_CH2_PROG_START	0x4D55#define LP5523_REG_CH3_PROG_START	0x4E56#define LP5523_REG_PROG_PAGE_SEL	0x4F57#define LP5523_REG_PROG_MEM		0x5058 59/* Bit description in registers */60#define LP5523_ENABLE			0x4061#define LP5523_AUTO_INC			0x4062#define LP5523_PWR_SAVE			0x2063#define LP5523_PWM_PWR_SAVE		0x0464#define LP5523_CP_MODE_MASK		0x1865#define LP5523_CP_MODE_SHIFT		366#define LP5523_AUTO_CLK			0x0267#define LP5523_DEFAULT_CONFIG \68	(LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE)69 70#define LP5523_EN_LEDTEST		0x8071#define LP5523_LEDTEST_DONE		0x8072#define LP5523_RESET			0xFF73#define LP5523_ADC_SHORTCIRC_LIM	8074#define LP5523_EXT_CLK_USED		0x0875#define LP5523_ENG_STATUS_MASK		0x0776 77static int lp5523_init_program_engine(struct lp55xx_chip *chip);78 79static int lp5523_post_init_device(struct lp55xx_chip *chip)80{81	int ret;82	int val;83 84	ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE);85	if (ret)86		return ret;87 88	/* Chip startup time is 500 us, 1 - 2 ms gives some margin */89	usleep_range(1000, 2000);90 91	val = LP5523_DEFAULT_CONFIG;92	val |= (chip->pdata->charge_pump_mode << LP5523_CP_MODE_SHIFT) & LP5523_CP_MODE_MASK;93 94	ret = lp55xx_write(chip, LP5523_REG_CONFIG, val);95	if (ret)96		return ret;97 98	/* turn on all leds */99	ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01);100	if (ret)101		return ret;102 103	ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);104	if (ret)105		return ret;106 107	return lp5523_init_program_engine(chip);108}109 110static void lp5523_run_engine(struct lp55xx_chip *chip, bool start)111{112	/* stop engine */113	if (!start) {114		lp55xx_stop_engine(chip);115		lp55xx_turn_off_channels(chip);116		return;117	}118 119	lp55xx_run_engine_common(chip);120}121 122static int lp5523_init_program_engine(struct lp55xx_chip *chip)123{124	int i;125	int j;126	int ret;127	u8 status;128	/* one pattern per engine setting LED MUX start and stop addresses */129	static const u8 pattern[][LP55xx_BYTES_PER_PAGE] =  {130		{ 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},131		{ 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},132		{ 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},133	};134 135	/* hardcode 32 bytes of memory for each engine from program memory */136	ret = lp55xx_write(chip, LP5523_REG_CH1_PROG_START, 0x00);137	if (ret)138		return ret;139 140	ret = lp55xx_write(chip, LP5523_REG_CH2_PROG_START, 0x10);141	if (ret)142		return ret;143 144	ret = lp55xx_write(chip, LP5523_REG_CH3_PROG_START, 0x20);145	if (ret)146		return ret;147 148	/* write LED MUX address space for each engine */149	for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {150		chip->engine_idx = i;151		lp55xx_load_engine(chip);152 153		for (j = 0; j < LP55xx_BYTES_PER_PAGE; j++) {154			ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + j,155					pattern[i - 1][j]);156			if (ret)157				goto out;158		}159	}160 161	lp5523_run_engine(chip, true);162 163	/* Let the programs run for couple of ms and check the engine status */164	usleep_range(3000, 6000);165	ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);166	if (ret)167		goto out;168	status &= LP5523_ENG_STATUS_MASK;169 170	if (status != LP5523_ENG_STATUS_MASK) {171		dev_err(&chip->cl->dev,172			"could not configure LED engine, status = 0x%.2x\n",173			status);174		ret = -1;175	}176 177out:178	lp55xx_stop_all_engine(chip);179	return ret;180}181 182static ssize_t lp5523_selftest(struct device *dev,183			       struct device_attribute *attr,184			       char *buf)185{186	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));187	struct lp55xx_chip *chip = led->chip;188	struct lp55xx_platform_data *pdata = chip->pdata;189	int ret, pos = 0;190	u8 status, adc, vdd, i;191 192	guard(mutex)(&chip->lock);193 194	ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);195	if (ret < 0)196		return sysfs_emit(buf, "FAIL\n");197 198	/* Check that ext clock is really in use if requested */199	if (pdata->clock_mode == LP55XX_CLOCK_EXT) {200		if  ((status & LP5523_EXT_CLK_USED) == 0)201			return sysfs_emit(buf, "FAIL\n");202	}203 204	/* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */205	lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16);206	usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */207	ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);208	if (ret < 0)209		return sysfs_emit(buf, "FAIL\n");210 211	if (!(status & LP5523_LEDTEST_DONE))212		usleep_range(3000, 6000); /* Was not ready. Wait little bit */213 214	ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd);215	if (ret < 0)216		return sysfs_emit(buf, "FAIL\n");217 218	vdd--;	/* There may be some fluctuation in measurement */219 220	for (i = 0; i < pdata->num_channels; i++) {221		/* Skip disabled channels */222		if (pdata->led_config[i].led_current == 0)223			continue;224 225		/* Set default current */226		lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr,227			pdata->led_config[i].led_current);228 229		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,230			     0xff);231		/* let current stabilize 2 - 4ms before measurements start */232		usleep_range(2000, 4000);233		lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL,234			     LP5523_EN_LEDTEST | led->chan_nr);235		/* ADC conversion time is 2.7 ms typically */236		usleep_range(3000, 6000);237		ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);238		if (ret < 0)239			return sysfs_emit(buf, "FAIL\n");240 241		if (!(status & LP5523_LEDTEST_DONE))242			usleep_range(3000, 6000); /* Was not ready. Wait. */243 244		ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc);245		if (ret < 0)246			return sysfs_emit(buf, "FAIL\n");247 248		if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM)249			pos += sysfs_emit_at(buf, pos, "LED %d FAIL\n",250					     led->chan_nr);251 252		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,253			     0x00);254 255		/* Restore current */256		lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr,257			     led->led_current);258		led++;259	}260 261	return pos == 0 ? sysfs_emit(buf, "OK\n") : pos;262}263 264LP55XX_DEV_ATTR_ENGINE_MODE(1);265LP55XX_DEV_ATTR_ENGINE_MODE(2);266LP55XX_DEV_ATTR_ENGINE_MODE(3);267LP55XX_DEV_ATTR_ENGINE_LEDS(1);268LP55XX_DEV_ATTR_ENGINE_LEDS(2);269LP55XX_DEV_ATTR_ENGINE_LEDS(3);270LP55XX_DEV_ATTR_ENGINE_LOAD(1);271LP55XX_DEV_ATTR_ENGINE_LOAD(2);272LP55XX_DEV_ATTR_ENGINE_LOAD(3);273static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);274LP55XX_DEV_ATTR_MASTER_FADER(1);275LP55XX_DEV_ATTR_MASTER_FADER(2);276LP55XX_DEV_ATTR_MASTER_FADER(3);277static LP55XX_DEV_ATTR_RW(master_fader_leds, lp55xx_show_master_fader_leds,278			  lp55xx_store_master_fader_leds);279 280static struct attribute *lp5523_attributes[] = {281	&dev_attr_engine1_mode.attr,282	&dev_attr_engine2_mode.attr,283	&dev_attr_engine3_mode.attr,284	&dev_attr_engine1_load.attr,285	&dev_attr_engine2_load.attr,286	&dev_attr_engine3_load.attr,287	&dev_attr_engine1_leds.attr,288	&dev_attr_engine2_leds.attr,289	&dev_attr_engine3_leds.attr,290	&dev_attr_selftest.attr,291	&dev_attr_master_fader1.attr,292	&dev_attr_master_fader2.attr,293	&dev_attr_master_fader3.attr,294	&dev_attr_master_fader_leds.attr,295	NULL,296};297 298static const struct attribute_group lp5523_group = {299	.attrs = lp5523_attributes,300};301 302/* Chip specific configurations */303static struct lp55xx_device_config lp5523_cfg = {304	.reg_op_mode = {305		.addr = LP5523_REG_OP_MODE,306	},307	.reg_exec = {308		.addr = LP5523_REG_ENABLE,309	},310	.engine_busy = {311		.addr = LP5523_REG_STATUS,312		.mask  = LP5523_ENGINE_BUSY,313	},314	.reset = {315		.addr = LP5523_REG_RESET,316		.val  = LP5523_RESET,317	},318	.enable = {319		.addr = LP5523_REG_ENABLE,320		.val  = LP5523_ENABLE,321	},322	.prog_mem_base = {323		.addr = LP5523_REG_PROG_MEM,324	},325	.reg_led_pwm_base = {326		.addr = LP5523_REG_LED_PWM_BASE,327	},328	.reg_led_current_base = {329		.addr = LP5523_REG_LED_CURRENT_BASE,330	},331	.reg_master_fader_base = {332		.addr = LP5523_REG_MASTER_FADER_BASE,333	},334	.reg_led_ctrl_base = {335		.addr = LP5523_REG_LED_CTRL_BASE,336	},337	.pages_per_engine   = LP5523_PAGES_PER_ENGINE,338	.max_channel  = LP5523_MAX_LEDS,339	.post_init_device   = lp5523_post_init_device,340	.brightness_fn      = lp55xx_led_brightness,341	.multicolor_brightness_fn = lp55xx_multicolor_brightness,342	.set_led_current    = lp55xx_set_led_current,343	.firmware_cb        = lp55xx_firmware_loaded_cb,344	.run_engine         = lp5523_run_engine,345	.dev_attr_group     = &lp5523_group,346};347 348static const struct i2c_device_id lp5523_id[] = {349	{ "lp5523",  .driver_data = (kernel_ulong_t)&lp5523_cfg, },350	{ "lp55231", .driver_data = (kernel_ulong_t)&lp5523_cfg, },351	{ }352};353 354MODULE_DEVICE_TABLE(i2c, lp5523_id);355 356static const struct of_device_id of_lp5523_leds_match[] = {357	{ .compatible = "national,lp5523", .data = &lp5523_cfg, },358	{ .compatible = "ti,lp55231", .data = &lp5523_cfg, },359	{},360};361 362MODULE_DEVICE_TABLE(of, of_lp5523_leds_match);363 364static struct i2c_driver lp5523_driver = {365	.driver = {366		.name	= "lp5523x",367		.of_match_table = of_lp5523_leds_match,368	},369	.probe		= lp55xx_probe,370	.remove		= lp55xx_remove,371	.id_table	= lp5523_id,372};373 374module_i2c_driver(lp5523_driver);375 376MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");377MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");378MODULE_DESCRIPTION("LP5523 LED engine");379MODULE_LICENSE("GPL");380