brintos

brintos / linux-shallow public Read only

0
0
Text · 17.1 KiB · f9d9844 Raw
673 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Awinic AW20036/AW20054/AW20072/AW20108 LED driver4 *5 * Copyright (c) 2023, SberDevices. All Rights Reserved.6 *7 * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru>8 */9 10#include <linux/bitfield.h>11#include <linux/bits.h>12#include <linux/container_of.h>13#include <linux/gpio/consumer.h>14#include <linux/i2c.h>15#include <linux/leds.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/mutex.h>19#include <linux/regmap.h>20#include <linux/time.h>21#include <linux/units.h>22 23#define AW200XX_DIM_MAX                  (BIT(6) - 1)24#define AW200XX_FADE_MAX                 (BIT(8) - 1)25#define AW200XX_IMAX_DEFAULT_uA          6000026#define AW200XX_IMAX_MAX_uA              16000027#define AW200XX_IMAX_MIN_uA              330028 29/* Page 0 */30#define AW200XX_REG_PAGE0_BASE 0xc00031 32/* Select page register */33#define AW200XX_REG_PAGE       0xF034#define AW200XX_PAGE_MASK      (GENMASK(7, 6) | GENMASK(2, 0))35#define AW200XX_PAGE_SHIFT     036#define AW200XX_NUM_PAGES      637#define AW200XX_PAGE_SIZE      25638#define AW200XX_REG(page, reg) \39	(AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg))40#define AW200XX_REG_MAX \41	AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1)42#define AW200XX_PAGE0 043#define AW200XX_PAGE1 144#define AW200XX_PAGE2 245#define AW200XX_PAGE3 346#define AW200XX_PAGE4 447#define AW200XX_PAGE5 548 49/* Chip ID register */50#define AW200XX_REG_IDR       AW200XX_REG(AW200XX_PAGE0, 0x00)51#define AW200XX_IDR_CHIPID    0x1852 53/* Sleep mode register */54#define AW200XX_REG_SLPCR     AW200XX_REG(AW200XX_PAGE0, 0x01)55#define AW200XX_SLPCR_ACTIVE  0x0056 57/* Reset register */58#define AW200XX_REG_RSTR      AW200XX_REG(AW200XX_PAGE0, 0x02)59#define AW200XX_RSTR_RESET    0x0160 61/* Global current configuration register */62#define AW200XX_REG_GCCR        AW200XX_REG(AW200XX_PAGE0, 0x03)63#define AW200XX_GCCR_IMAX_MASK  GENMASK(7, 4)64#define AW200XX_GCCR_IMAX(x)    ((x) << 4)65#define AW200XX_GCCR_ALLON      BIT(3)66 67/* Fast clear display control register */68#define AW200XX_REG_FCD       AW200XX_REG(AW200XX_PAGE0, 0x04)69#define AW200XX_FCD_CLEAR     0x0170 71/* Display size configuration */72#define AW200XX_REG_DSIZE          AW200XX_REG(AW200XX_PAGE0, 0x80)73#define AW200XX_DSIZE_COLUMNS_MAX  1274 75#define AW200XX_LED2REG(x, columns) \76	((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns))))77 78/* DIM current configuration register on page 1 */79#define AW200XX_REG_DIM_PAGE1(x, columns) \80	AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns))81 82/*83 * DIM current configuration register (page 4).84 * The even address for current DIM configuration.85 * The odd address for current FADE configuration86 */87#define AW200XX_REG_DIM(x, columns) \88	AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2)89#define AW200XX_REG_DIM2FADE(x) ((x) + 1)90#define AW200XX_REG_FADE2DIM(fade) \91	DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX)92 93/*94 * Duty ratio of display scan (see p.15 of datasheet for formula):95 *   duty = (592us / 600.5us) * (1 / (display_rows + 1))96 *97 * Multiply to 1000 (MILLI) to improve the accuracy of calculations.98 */99#define AW200XX_DUTY_RATIO(rows) \100	(((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI)101 102struct aw200xx_chipdef {103	u32 channels;104	u32 display_size_rows_max;105	u32 display_size_columns;106};107 108struct aw200xx_led {109	struct led_classdev cdev;110	struct aw200xx *chip;111	int dim;112	u32 num;113};114 115struct aw200xx {116	const struct aw200xx_chipdef *cdef;117	struct i2c_client *client;118	struct regmap *regmap;119	struct mutex mutex;120	u32 num_leds;121	u32 display_rows;122	struct gpio_desc *hwen;123	struct aw200xx_led leds[] __counted_by(num_leds);124};125 126static ssize_t dim_show(struct device *dev, struct device_attribute *devattr,127			char *buf)128{129	struct led_classdev *cdev = dev_get_drvdata(dev);130	struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);131	int dim = led->dim;132 133	if (dim < 0)134		return sysfs_emit(buf, "auto\n");135 136	return sysfs_emit(buf, "%d\n", dim);137}138 139static ssize_t dim_store(struct device *dev, struct device_attribute *devattr,140			 const char *buf, size_t count)141{142	struct led_classdev *cdev = dev_get_drvdata(dev);143	struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);144	struct aw200xx *chip = led->chip;145	u32 columns = chip->cdef->display_size_columns;146	int dim;147	ssize_t ret;148 149	if (sysfs_streq(buf, "auto")) {150		dim = -1;151	} else {152		ret = kstrtoint(buf, 0, &dim);153		if (ret)154			return ret;155 156		if (dim > AW200XX_DIM_MAX)157			return -EINVAL;158	}159 160	mutex_lock(&chip->mutex);161 162	if (dim >= 0) {163		ret = regmap_write(chip->regmap,164				   AW200XX_REG_DIM_PAGE1(led->num, columns),165				   dim);166		if (ret)167			goto out_unlock;168	}169 170	led->dim = dim;171	ret = count;172 173out_unlock:174	mutex_unlock(&chip->mutex);175	return ret;176}177static DEVICE_ATTR_RW(dim);178 179static struct attribute *dim_attrs[] = {180	&dev_attr_dim.attr,181	NULL182};183ATTRIBUTE_GROUPS(dim);184 185static int aw200xx_brightness_set(struct led_classdev *cdev,186				  enum led_brightness brightness)187{188	struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);189	struct aw200xx *chip = led->chip;190	int dim;191	u32 reg;192	int ret;193 194	mutex_lock(&chip->mutex);195 196	reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns);197 198	dim = led->dim;199	if (dim < 0)200		dim = AW200XX_REG_FADE2DIM(brightness);201 202	ret = regmap_write(chip->regmap, reg, dim);203	if (ret)204		goto out_unlock;205 206	ret = regmap_write(chip->regmap,207			   AW200XX_REG_DIM2FADE(reg), brightness);208 209out_unlock:210	mutex_unlock(&chip->mutex);211 212	return ret;213}214 215static u32 aw200xx_imax_from_global(const struct aw200xx *const chip,216				    u32 global_imax_uA)217{218	u64 led_imax_uA;219 220	/*221	 * The output current of each LED (see p.14 of datasheet for formula):222	 *   Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty223	 *224	 * The value of duty is determined by the following formula:225	 *   duty = (592us / 600.5us) * (1 / (display_rows + 1))226	 *227	 * Calculated for the maximum values of fade and dim.228	 * We divide by 1000 because we earlier multiplied by 1000 to improve229	 * accuracy when calculating the duty.230	 */231	led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows);232	do_div(led_imax_uA, MILLI);233 234	return led_imax_uA;235}236 237static u32 aw200xx_imax_to_global(const struct aw200xx *const chip,238				  u32 led_imax_uA)239{240	u32 duty = AW200XX_DUTY_RATIO(chip->display_rows);241 242	/* The output current of each LED (see p.14 of datasheet for formula) */243	return (led_imax_uA * 1000U) / duty;244}245 246#define AW200XX_IMAX_MULTIPLIER1    10000247#define AW200XX_IMAX_MULTIPLIER2    3333248#define AW200XX_IMAX_BASE_VAL1      0249#define AW200XX_IMAX_BASE_VAL2      8250 251/*252 * The AW200XX has a 4-bit register (GCCR) to configure the global current,253 * which ranges from 3.3mA to 160mA. The following table indicates the values254 * of the global current, divided into two parts:255 *256 * +-----------+-----------------+-----------+-----------------+257 * | reg value | global max (mA) | reg value | global max (mA) |258 * +-----------+-----------------+-----------+-----------------+259 * | 0         | 10              | 8         | 3.3             |260 * | 1         | 20              | 9         | 6.7             |261 * | 2         | 30              | 10        | 10              |262 * | 3         | 40              | 11        | 13.3            |263 * | 4         | 60              | 12        | 20              |264 * | 5         | 80              | 13        | 26.7            |265 * | 6         | 120             | 14        | 40              |266 * | 7         | 160             | 15        | 53.3            |267 * +-----------+-----------------+-----------+-----------------+268 *269 * The left part  with a multiplier of 10, and the right part  with a multiplier270 * of 3.3.271 * So we have two formulas to calculate the global current:272 *   for the left part of the table:273 *     imax = coefficient * 10274 *275 *   for the right part of the table:276 *     imax = coefficient * 3.3277 *278 * The coefficient table consists of the following values:279 *   1, 2, 3, 4, 6, 8, 12, 16.280 */281static int aw200xx_set_imax(const struct aw200xx *const chip,282			    u32 led_imax_uA)283{284	u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA);285	static const u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16};286	u32 gccr_imax = UINT_MAX;287	u32 cur_imax = 0;288	int i;289 290	for (i = 0; i < ARRAY_SIZE(coeff_table); i++) {291		u32 imax;292 293		/* select closest ones */294		imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1;295		if (g_imax_uA >= imax && imax > cur_imax) {296			cur_imax = imax;297			gccr_imax = i + AW200XX_IMAX_BASE_VAL1;298		}299 300		imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2;301		imax = DIV_ROUND_CLOSEST(imax, 100) * 100;302		if (g_imax_uA >= imax && imax > cur_imax) {303			cur_imax = imax;304			gccr_imax = i + AW200XX_IMAX_BASE_VAL2;305		}306	}307 308	if (gccr_imax == UINT_MAX)309		return -EINVAL;310 311	return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,312				  AW200XX_GCCR_IMAX_MASK,313				  AW200XX_GCCR_IMAX(gccr_imax));314}315 316static int aw200xx_chip_reset(const struct aw200xx *const chip)317{318	int ret;319 320	ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET);321	if (ret)322		return ret;323 324	/* According to the datasheet software reset takes at least 1ms */325	fsleep(1000);326 327	regcache_mark_dirty(chip->regmap);328	return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR);329}330 331static int aw200xx_chip_init(const struct aw200xx *const chip)332{333	int ret;334 335	ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE,336			   chip->display_rows - 1);337	if (ret)338		return ret;339 340	ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR,341			   AW200XX_SLPCR_ACTIVE);342	if (ret)343		return ret;344 345	return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,346				  AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON);347}348 349static int aw200xx_chip_check(const struct aw200xx *const chip)350{351	struct device *dev = &chip->client->dev;352	u32 chipid;353	int ret;354 355	ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid);356	if (ret)357		return dev_err_probe(dev, ret, "Failed to read chip ID\n");358 359	if (chipid != AW200XX_IDR_CHIPID)360		return dev_err_probe(dev, -ENODEV,361				     "Chip reported wrong ID: %x\n", chipid);362 363	return 0;364}365 366static void aw200xx_enable(const struct aw200xx *const chip)367{368	gpiod_set_value_cansleep(chip->hwen, 1);369 370	/*371	 * After HWEN pin set high the chip begins to load the OTP information,372	 * which takes 200us to complete. About 200us wait time is needed for373	 * internal oscillator startup and display SRAM initialization. After374	 * display SRAM initialization, the registers in page1 to page5 can be375	 * configured via i2c interface.376	 */377	fsleep(400);378}379 380static void aw200xx_disable(const struct aw200xx *const chip)381{382	return gpiod_set_value_cansleep(chip->hwen, 0);383}384 385static int aw200xx_probe_get_display_rows(struct device *dev,386					  struct aw200xx *chip)387{388	struct fwnode_handle *child;389	u32 max_source = 0;390 391	device_for_each_child_node(dev, child) {392		u32 source;393		int ret;394 395		ret = fwnode_property_read_u32(child, "reg", &source);396		if (ret || source >= chip->cdef->channels)397			continue;398 399		max_source = max(max_source, source);400	}401 402	if (max_source == 0)403		return -EINVAL;404 405	chip->display_rows = max_source / chip->cdef->display_size_columns + 1;406 407	return 0;408}409 410static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip)411{412	struct fwnode_handle *child;413	u32 current_min, current_max, min_uA;414	int ret;415	int i;416 417	ret = aw200xx_probe_get_display_rows(dev, chip);418	if (ret)419		return dev_err_probe(dev, ret,420				     "No valid led definitions found\n");421 422	current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA);423	current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA);424	min_uA = UINT_MAX;425	i = 0;426 427	device_for_each_child_node(dev, child) {428		struct led_init_data init_data = {};429		struct aw200xx_led *led;430		u32 source, imax;431 432		ret = fwnode_property_read_u32(child, "reg", &source);433		if (ret) {434			dev_err(dev, "Missing reg property\n");435			chip->num_leds--;436			continue;437		}438 439		if (source >= chip->cdef->channels) {440			dev_err(dev, "LED reg %u out of range (max %u)\n",441				source, chip->cdef->channels);442			chip->num_leds--;443			continue;444		}445 446		ret = fwnode_property_read_u32(child, "led-max-microamp",447					       &imax);448		if (ret) {449			dev_info(&chip->client->dev,450				 "DT property led-max-microamp is missing\n");451		} else if (imax < current_min || imax > current_max) {452			dev_err(dev, "Invalid value %u for led-max-microamp\n",453				imax);454			chip->num_leds--;455			continue;456		} else {457			min_uA = min(min_uA, imax);458		}459 460		led = &chip->leds[i];461		led->dim = -1;462		led->num = source;463		led->chip = chip;464		led->cdev.brightness_set_blocking = aw200xx_brightness_set;465		led->cdev.max_brightness = AW200XX_FADE_MAX;466		led->cdev.groups = dim_groups;467		init_data.fwnode = child;468 469		ret = devm_led_classdev_register_ext(dev, &led->cdev,470						     &init_data);471		if (ret) {472			fwnode_handle_put(child);473			break;474		}475 476		i++;477	}478 479	if (!chip->num_leds)480		return -EINVAL;481 482	if (min_uA == UINT_MAX) {483		min_uA = aw200xx_imax_from_global(chip,484						  AW200XX_IMAX_DEFAULT_uA);485	}486 487	return aw200xx_set_imax(chip, min_uA);488}489 490static const struct regmap_range_cfg aw200xx_ranges[] = {491	{492		.name = "aw200xx",493		.range_min = 0,494		.range_max = AW200XX_REG_MAX,495		.selector_reg = AW200XX_REG_PAGE,496		.selector_mask = AW200XX_PAGE_MASK,497		.selector_shift = AW200XX_PAGE_SHIFT,498		.window_start = 0,499		.window_len = AW200XX_PAGE_SIZE,500	},501};502 503static const struct regmap_range aw200xx_writeonly_ranges[] = {504	regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX),505};506 507static const struct regmap_access_table aw200xx_readable_table = {508	.no_ranges = aw200xx_writeonly_ranges,509	.n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges),510};511 512static const struct regmap_range aw200xx_readonly_ranges[] = {513	regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR),514};515 516static const struct regmap_access_table aw200xx_writeable_table = {517	.no_ranges = aw200xx_readonly_ranges,518	.n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges),519};520 521static const struct regmap_config aw200xx_regmap_config = {522	.reg_bits = 8,523	.val_bits = 8,524	.max_register = AW200XX_REG_MAX,525	.ranges = aw200xx_ranges,526	.num_ranges = ARRAY_SIZE(aw200xx_ranges),527	.rd_table = &aw200xx_readable_table,528	.wr_table = &aw200xx_writeable_table,529	.cache_type = REGCACHE_MAPLE,530	.disable_locking = true,531};532 533static void aw200xx_chip_reset_action(void *data)534{535	aw200xx_chip_reset(data);536}537 538static void aw200xx_disable_action(void *data)539{540	aw200xx_disable(data);541}542 543static int aw200xx_probe(struct i2c_client *client)544{545	const struct aw200xx_chipdef *cdef;546	struct aw200xx *chip;547	int count;548	int ret;549 550	cdef = device_get_match_data(&client->dev);551	if (!cdef)552		return -ENODEV;553 554	count = device_get_child_node_count(&client->dev);555	if (!count || count > cdef->channels)556		return dev_err_probe(&client->dev, -EINVAL,557				     "Incorrect number of leds (%d)", count);558 559	chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count),560			    GFP_KERNEL);561	if (!chip)562		return -ENOMEM;563 564	chip->cdef = cdef;565	chip->num_leds = count;566	chip->client = client;567	i2c_set_clientdata(client, chip);568 569	chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config);570	if (IS_ERR(chip->regmap))571		return PTR_ERR(chip->regmap);572 573	chip->hwen = devm_gpiod_get_optional(&client->dev, "enable",574					     GPIOD_OUT_HIGH);575	if (IS_ERR(chip->hwen))576		return dev_err_probe(&client->dev, PTR_ERR(chip->hwen),577				     "Cannot get enable GPIO");578 579	aw200xx_enable(chip);580 581	ret = devm_add_action(&client->dev, aw200xx_disable_action, chip);582	if (ret)583		return ret;584 585	ret = aw200xx_chip_check(chip);586	if (ret)587		return ret;588 589	ret = devm_mutex_init(&client->dev, &chip->mutex);590	if (ret)591		return ret;592 593	/* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */594	mutex_lock(&chip->mutex);595 596	ret = aw200xx_chip_reset(chip);597	if (ret)598		goto out_unlock;599 600	ret = devm_add_action(&client->dev, aw200xx_chip_reset_action, chip);601	if (ret)602		goto out_unlock;603 604	ret = aw200xx_probe_fw(&client->dev, chip);605	if (ret)606		goto out_unlock;607 608	ret = aw200xx_chip_init(chip);609 610out_unlock:611	if (ret)612		aw200xx_disable(chip);613 614	mutex_unlock(&chip->mutex);615	return ret;616}617 618static const struct aw200xx_chipdef aw20036_cdef = {619	.channels = 36,620	.display_size_rows_max = 3,621	.display_size_columns = 12,622};623 624static const struct aw200xx_chipdef aw20054_cdef = {625	.channels = 54,626	.display_size_rows_max = 6,627	.display_size_columns = 9,628};629 630static const struct aw200xx_chipdef aw20072_cdef = {631	.channels = 72,632	.display_size_rows_max = 6,633	.display_size_columns = 12,634};635 636static const struct aw200xx_chipdef aw20108_cdef = {637	.channels = 108,638	.display_size_rows_max = 9,639	.display_size_columns = 12,640};641 642static const struct i2c_device_id aw200xx_id[] = {643	{ "aw20036" },644	{ "aw20054" },645	{ "aw20072" },646	{ "aw20108" },647	{}648};649MODULE_DEVICE_TABLE(i2c, aw200xx_id);650 651static const struct of_device_id aw200xx_match_table[] = {652	{ .compatible = "awinic,aw20036", .data = &aw20036_cdef, },653	{ .compatible = "awinic,aw20054", .data = &aw20054_cdef, },654	{ .compatible = "awinic,aw20072", .data = &aw20072_cdef, },655	{ .compatible = "awinic,aw20108", .data = &aw20108_cdef, },656	{}657};658MODULE_DEVICE_TABLE(of, aw200xx_match_table);659 660static struct i2c_driver aw200xx_driver = {661	.driver = {662		.name = "aw200xx",663		.of_match_table = aw200xx_match_table,664	},665	.probe = aw200xx_probe,666	.id_table = aw200xx_id,667};668module_i2c_driver(aw200xx_driver);669 670MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>");671MODULE_DESCRIPTION("AW200XX LED driver");672MODULE_LICENSE("GPL");673