brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 94422c9 Raw
350 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2022 Richtek Technology Corp.4 *5 * Author: ChiaEn Wu <chiaen_wu@richtek.com>6 */7 8#include <linux/backlight.h>9#include <linux/bitfield.h>10#include <linux/bits.h>11#include <linux/gpio/consumer.h>12#include <linux/kernel.h>13#include <linux/minmax.h>14#include <linux/mod_devicetable.h>15#include <linux/module.h>16#include <linux/platform_device.h>17#include <linux/property.h>18#include <linux/regmap.h>19 20#define MT6370_REG_DEV_INFO		0x10021#define MT6370_REG_BL_EN		0x1A022#define MT6370_REG_BL_BSTCTRL		0x1A123#define MT6370_REG_BL_PWM		0x1A224#define MT6370_REG_BL_DIM2		0x1A425 26#define MT6370_VENID_MASK		GENMASK(7, 4)27#define MT6370_BL_EXT_EN_MASK		BIT(7)28#define MT6370_BL_EN_MASK		BIT(6)29#define MT6370_BL_CODE_MASK		BIT(0)30#define MT6370_BL_CH_MASK		GENMASK(5, 2)31#define MT6370_BL_CH_SHIFT		232#define MT6370_BL_DIM2_COMMON_MASK	GENMASK(2, 0)33#define MT6370_BL_DIM2_COMMON_SHIFT	334#define MT6370_BL_DIM2_6372_MASK	GENMASK(5, 0)35#define MT6370_BL_DIM2_6372_SHIFT	636#define MT6370_BL_PWM_EN_MASK		BIT(7)37#define MT6370_BL_PWM_HYS_EN_MASK	BIT(2)38#define MT6370_BL_PWM_HYS_SEL_MASK	GENMASK(1, 0)39#define MT6370_BL_OVP_EN_MASK		BIT(7)40#define MT6370_BL_OVP_SEL_MASK		GENMASK(6, 5)41#define MT6370_BL_OVP_SEL_SHIFT		542#define MT6370_BL_OC_EN_MASK		BIT(3)43#define MT6370_BL_OC_SEL_MASK		GENMASK(2, 1)44#define MT6370_BL_OC_SEL_SHIFT		145 46#define MT6370_BL_PWM_HYS_TH_MIN_STEP	147#define MT6370_BL_PWM_HYS_TH_MAX_STEP	6448#define MT6370_BL_OVP_MIN_UV		1700000049#define MT6370_BL_OVP_MAX_UV		2900000050#define MT6370_BL_OVP_STEP_UV		400000051#define MT6370_BL_OCP_MIN_UA		90000052#define MT6370_BL_OCP_MAX_UA		180000053#define MT6370_BL_OCP_STEP_UA		30000054#define MT6370_BL_MAX_COMMON_BRIGHTNESS	204855#define MT6370_BL_MAX_6372_BRIGHTNESS	1638456#define MT6370_BL_MAX_CH		1557 58enum {59	MT6370_VID_COMMON = 1,60	MT6370_VID_6372,61};62 63struct mt6370_priv {64	u8 dim2_mask;65	u8 dim2_shift;66	int def_max_brightness;67	struct backlight_device *bl;68	struct device *dev;69	struct gpio_desc *enable_gpio;70	struct regmap *regmap;71};72 73static int mt6370_bl_update_status(struct backlight_device *bl_dev)74{75	struct mt6370_priv *priv = bl_get_data(bl_dev);76	int brightness = backlight_get_brightness(bl_dev);77	unsigned int enable_val;78	u8 brightness_val[2];79	int ret;80 81	if (brightness) {82		brightness_val[0] = (brightness - 1) & priv->dim2_mask;83		brightness_val[1] = (brightness - 1) >> priv->dim2_shift;84 85		ret = regmap_raw_write(priv->regmap, MT6370_REG_BL_DIM2,86				       brightness_val, sizeof(brightness_val));87		if (ret)88			return ret;89	}90 91	gpiod_set_value(priv->enable_gpio, !!brightness);92 93	enable_val = brightness ? MT6370_BL_EN_MASK : 0;94	return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN,95				  MT6370_BL_EN_MASK, enable_val);96}97 98static int mt6370_bl_get_brightness(struct backlight_device *bl_dev)99{100	struct mt6370_priv *priv = bl_get_data(bl_dev);101	unsigned int enable;102	u8 brightness_val[2];103	int brightness, ret;104 105	ret = regmap_read(priv->regmap, MT6370_REG_BL_EN, &enable);106	if (ret)107		return ret;108 109	if (!(enable & MT6370_BL_EN_MASK))110		return 0;111 112	ret = regmap_raw_read(priv->regmap, MT6370_REG_BL_DIM2,113			      brightness_val, sizeof(brightness_val));114	if (ret)115		return ret;116 117	brightness = brightness_val[1] << priv->dim2_shift;118	brightness += brightness_val[0] & priv->dim2_mask;119 120	return brightness + 1;121}122 123static const struct backlight_ops mt6370_bl_ops = {124	.options = BL_CORE_SUSPENDRESUME,125	.update_status = mt6370_bl_update_status,126	.get_brightness = mt6370_bl_get_brightness,127};128 129static int mt6370_init_backlight_properties(struct mt6370_priv *priv,130					    struct backlight_properties *props)131{132	struct device *dev = priv->dev;133	u8 prop_val;134	u32 brightness, ovp_uV, ocp_uA;135	unsigned int mask, val;136	int ret;137 138	/* Vendor optional properties */139	val = 0;140	if (device_property_read_bool(dev, "mediatek,bled-pwm-enable"))141		val |= MT6370_BL_PWM_EN_MASK;142 143	if (device_property_read_bool(dev, "mediatek,bled-pwm-hys-enable"))144		val |= MT6370_BL_PWM_HYS_EN_MASK;145 146	ret = device_property_read_u8(dev,147				      "mediatek,bled-pwm-hys-input-th-steps",148				      &prop_val);149	if (!ret) {150		prop_val = clamp_val(prop_val,151				     MT6370_BL_PWM_HYS_TH_MIN_STEP,152				     MT6370_BL_PWM_HYS_TH_MAX_STEP);153		prop_val = prop_val <= 1 ? 0 :154			   prop_val <= 4 ? 1 :155			   prop_val <= 16 ? 2 : 3;156		val |= prop_val;157	}158 159	ret = regmap_update_bits(priv->regmap, MT6370_REG_BL_PWM,160				 val, val);161	if (ret)162		return ret;163 164	val = 0;165	if (device_property_read_bool(dev, "mediatek,bled-ovp-shutdown"))166		val |= MT6370_BL_OVP_EN_MASK;167 168	ret = device_property_read_u32(dev, "mediatek,bled-ovp-microvolt",169				       &ovp_uV);170	if (!ret) {171		ovp_uV = clamp_val(ovp_uV, MT6370_BL_OVP_MIN_UV,172				   MT6370_BL_OVP_MAX_UV);173		ovp_uV = DIV_ROUND_UP(ovp_uV - MT6370_BL_OVP_MIN_UV,174				      MT6370_BL_OVP_STEP_UV);175		val |= ovp_uV << MT6370_BL_OVP_SEL_SHIFT;176	}177 178	if (device_property_read_bool(dev, "mediatek,bled-ocp-shutdown"))179		val |= MT6370_BL_OC_EN_MASK;180 181	ret = device_property_read_u32(dev, "mediatek,bled-ocp-microamp",182				       &ocp_uA);183	if (!ret) {184		ocp_uA = clamp_val(ocp_uA, MT6370_BL_OCP_MIN_UA,185				   MT6370_BL_OCP_MAX_UA);186		ocp_uA = DIV_ROUND_UP(ocp_uA - MT6370_BL_OCP_MIN_UA,187				      MT6370_BL_OCP_STEP_UA);188		val |= ocp_uA << MT6370_BL_OC_SEL_SHIFT;189	}190 191	ret = regmap_update_bits(priv->regmap, MT6370_REG_BL_BSTCTRL,192				 val, val);193	if (ret)194		return ret;195 196	/* Common properties */197	ret = device_property_read_u32(dev, "max-brightness", &brightness);198	if (ret)199		brightness = priv->def_max_brightness;200 201	props->max_brightness = min_t(u32, brightness, priv->def_max_brightness);202 203	ret = device_property_read_u32(dev, "default-brightness", &brightness);204	if (ret)205		brightness = props->max_brightness;206 207	props->brightness = min_t(u32, brightness, props->max_brightness);208 209	val = 0;210	if (device_property_read_bool(dev, "mediatek,bled-exponential-mode-enable")) {211		val |= MT6370_BL_CODE_MASK;212		props->scale = BACKLIGHT_SCALE_NON_LINEAR;213	} else214		props->scale = BACKLIGHT_SCALE_LINEAR;215 216	ret = device_property_read_u8(dev, "mediatek,bled-channel-use",217				      &prop_val);218	if (ret) {219		dev_err(dev, "mediatek,bled-channel-use DT property missing\n");220		return ret;221	}222 223	if (!prop_val || prop_val > MT6370_BL_MAX_CH) {224		dev_err(dev,225			"No channel specified or over than upper bound (%d)\n",226			prop_val);227		return -EINVAL;228	}229 230	mask = MT6370_BL_EXT_EN_MASK | MT6370_BL_CH_MASK;231	val |= prop_val << MT6370_BL_CH_SHIFT;232 233	if (priv->enable_gpio)234		val |= MT6370_BL_EXT_EN_MASK;235 236	return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN, mask, val);237}238 239static int mt6370_check_vendor_info(struct mt6370_priv *priv)240{241	/*242	 * Because MT6372 uses 14 bits to control the brightness,243	 * MT6370 and MT6371 use 11 bits. This function is used244	 * to check the vendor's ID and set the relative hardware245	 * mask, shift and default maximum brightness value that246	 * should be used.247	 */248	unsigned int dev_info, hw_vid, of_vid;249	int ret;250 251	ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &dev_info);252	if (ret)253		return ret;254 255	of_vid = (uintptr_t)device_get_match_data(priv->dev);256	hw_vid = FIELD_GET(MT6370_VENID_MASK, dev_info);257	hw_vid = (hw_vid == 0x9 || hw_vid == 0xb) ? MT6370_VID_6372 : MT6370_VID_COMMON;258	if (hw_vid != of_vid)259		return dev_err_probe(priv->dev, -EINVAL,260				     "Buggy DT, wrong compatible string\n");261 262	if (hw_vid == MT6370_VID_6372) {263		priv->dim2_mask = MT6370_BL_DIM2_6372_MASK;264		priv->dim2_shift = MT6370_BL_DIM2_6372_SHIFT;265		priv->def_max_brightness = MT6370_BL_MAX_6372_BRIGHTNESS;266	} else {267		priv->dim2_mask = MT6370_BL_DIM2_COMMON_MASK;268		priv->dim2_shift = MT6370_BL_DIM2_COMMON_SHIFT;269		priv->def_max_brightness = MT6370_BL_MAX_COMMON_BRIGHTNESS;270	}271 272	return 0;273}274 275static int mt6370_bl_probe(struct platform_device *pdev)276{277	struct backlight_properties props = {278		.type = BACKLIGHT_RAW,279	};280	struct device *dev = &pdev->dev;281	struct mt6370_priv *priv;282	int ret;283 284	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);285	if (!priv)286		return -ENOMEM;287 288	priv->dev = dev;289 290	priv->regmap = dev_get_regmap(dev->parent, NULL);291	if (!priv->regmap)292		return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n");293 294	ret = mt6370_check_vendor_info(priv);295	if (ret)296		return dev_err_probe(dev, ret, "Failed to check vendor info\n");297 298	priv->enable_gpio = devm_gpiod_get_optional(dev, "enable",299						    GPIOD_OUT_HIGH);300	if (IS_ERR(priv->enable_gpio))301		return dev_err_probe(dev, PTR_ERR(priv->enable_gpio),302				     "Failed to get 'enable' gpio\n");303 304	ret = mt6370_init_backlight_properties(priv, &props);305	if (ret)306		return dev_err_probe(dev, ret,307				     "Failed to init backlight properties\n");308 309	priv->bl = devm_backlight_device_register(dev, pdev->name, dev, priv,310						  &mt6370_bl_ops, &props);311	if (IS_ERR(priv->bl))312		return dev_err_probe(dev, PTR_ERR(priv->bl),313				     "Failed to register backlight\n");314 315	backlight_update_status(priv->bl);316	platform_set_drvdata(pdev, priv);317 318	return 0;319}320 321static void mt6370_bl_remove(struct platform_device *pdev)322{323	struct mt6370_priv *priv = platform_get_drvdata(pdev);324	struct backlight_device *bl_dev = priv->bl;325 326	bl_dev->props.brightness = 0;327	backlight_update_status(priv->bl);328}329 330static const struct of_device_id mt6370_bl_of_match[] = {331	{ .compatible = "mediatek,mt6370-backlight", .data = (void *)MT6370_VID_COMMON },332	{ .compatible = "mediatek,mt6372-backlight", .data = (void *)MT6370_VID_6372 },333	{}334};335MODULE_DEVICE_TABLE(of, mt6370_bl_of_match);336 337static struct platform_driver mt6370_bl_driver = {338	.driver = {339		.name = "mt6370-backlight",340		.of_match_table = mt6370_bl_of_match,341	},342	.probe = mt6370_bl_probe,343	.remove_new = mt6370_bl_remove,344};345module_platform_driver(mt6370_bl_driver);346 347MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>");348MODULE_DESCRIPTION("MediaTek MT6370 Backlight Driver");349MODULE_LICENSE("GPL v2");350