0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/kernel.h>
0012 #include <linux/backlight.h>
0013 #include <linux/err.h>
0014 #include <linux/fb.h>
0015 #include <linux/mfd/tps65217.h>
0016 #include <linux/module.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/slab.h>
0019
0020 struct tps65217_bl {
0021 struct tps65217 *tps;
0022 struct device *dev;
0023 struct backlight_device *bl;
0024 bool is_enabled;
0025 };
0026
0027 static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
0028 {
0029 int rc;
0030
0031 rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
0032 TPS65217_WLEDCTRL1_ISINK_ENABLE,
0033 TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
0034 if (rc) {
0035 dev_err(tps65217_bl->dev,
0036 "failed to enable backlight: %d\n", rc);
0037 return rc;
0038 }
0039
0040 tps65217_bl->is_enabled = true;
0041
0042 dev_dbg(tps65217_bl->dev, "backlight enabled\n");
0043
0044 return 0;
0045 }
0046
0047 static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
0048 {
0049 int rc;
0050
0051 rc = tps65217_clear_bits(tps65217_bl->tps,
0052 TPS65217_REG_WLEDCTRL1,
0053 TPS65217_WLEDCTRL1_ISINK_ENABLE,
0054 TPS65217_PROTECT_NONE);
0055 if (rc) {
0056 dev_err(tps65217_bl->dev,
0057 "failed to disable backlight: %d\n", rc);
0058 return rc;
0059 }
0060
0061 tps65217_bl->is_enabled = false;
0062
0063 dev_dbg(tps65217_bl->dev, "backlight disabled\n");
0064
0065 return 0;
0066 }
0067
0068 static int tps65217_bl_update_status(struct backlight_device *bl)
0069 {
0070 struct tps65217_bl *tps65217_bl = bl_get_data(bl);
0071 int rc;
0072 int brightness = backlight_get_brightness(bl);
0073
0074 if (brightness > 0) {
0075 rc = tps65217_reg_write(tps65217_bl->tps,
0076 TPS65217_REG_WLEDCTRL2,
0077 brightness - 1,
0078 TPS65217_PROTECT_NONE);
0079 if (rc) {
0080 dev_err(tps65217_bl->dev,
0081 "failed to set brightness level: %d\n", rc);
0082 return rc;
0083 }
0084
0085 dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
0086
0087 if (!tps65217_bl->is_enabled)
0088 rc = tps65217_bl_enable(tps65217_bl);
0089 } else {
0090 rc = tps65217_bl_disable(tps65217_bl);
0091 }
0092
0093 return rc;
0094 }
0095
0096 static const struct backlight_ops tps65217_bl_ops = {
0097 .options = BL_CORE_SUSPENDRESUME,
0098 .update_status = tps65217_bl_update_status,
0099 };
0100
0101 static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
0102 struct tps65217_bl_pdata *pdata)
0103 {
0104 int rc;
0105
0106 rc = tps65217_bl_disable(tps65217_bl);
0107 if (rc)
0108 return rc;
0109
0110 switch (pdata->isel) {
0111 case TPS65217_BL_ISET1:
0112
0113 rc = tps65217_clear_bits(tps65217_bl->tps,
0114 TPS65217_REG_WLEDCTRL1,
0115 TPS65217_WLEDCTRL1_ISEL,
0116 TPS65217_PROTECT_NONE);
0117 if (rc) {
0118 dev_err(tps65217_bl->dev,
0119 "failed to select ISET1 current level: %d)\n",
0120 rc);
0121 return rc;
0122 }
0123
0124 dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
0125
0126 break;
0127
0128 case TPS65217_BL_ISET2:
0129
0130 rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
0131 TPS65217_WLEDCTRL1_ISEL,
0132 TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
0133 if (rc) {
0134 dev_err(tps65217_bl->dev,
0135 "failed to select ISET2 current level: %d\n",
0136 rc);
0137 return rc;
0138 }
0139
0140 dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
0141
0142 break;
0143
0144 default:
0145 dev_err(tps65217_bl->dev,
0146 "invalid value for current level: %d\n", pdata->isel);
0147 return -EINVAL;
0148 }
0149
0150
0151 rc = tps65217_set_bits(tps65217_bl->tps,
0152 TPS65217_REG_WLEDCTRL1,
0153 TPS65217_WLEDCTRL1_FDIM_MASK,
0154 pdata->fdim,
0155 TPS65217_PROTECT_NONE);
0156 if (rc) {
0157 dev_err(tps65217_bl->dev,
0158 "failed to select PWM dimming frequency: %d\n",
0159 rc);
0160 return rc;
0161 }
0162
0163 return 0;
0164 }
0165
0166 #ifdef CONFIG_OF
0167 static struct tps65217_bl_pdata *
0168 tps65217_bl_parse_dt(struct platform_device *pdev)
0169 {
0170 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
0171 struct device_node *node;
0172 struct tps65217_bl_pdata *pdata, *err;
0173 u32 val;
0174
0175 node = of_get_child_by_name(tps->dev->of_node, "backlight");
0176 if (!node)
0177 return ERR_PTR(-ENODEV);
0178
0179 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
0180 if (!pdata) {
0181 err = ERR_PTR(-ENOMEM);
0182 goto err;
0183 }
0184
0185 pdata->isel = TPS65217_BL_ISET1;
0186 if (!of_property_read_u32(node, "isel", &val)) {
0187 if (val < TPS65217_BL_ISET1 ||
0188 val > TPS65217_BL_ISET2) {
0189 dev_err(&pdev->dev,
0190 "invalid 'isel' value in the device tree\n");
0191 err = ERR_PTR(-EINVAL);
0192 goto err;
0193 }
0194
0195 pdata->isel = val;
0196 }
0197
0198 pdata->fdim = TPS65217_BL_FDIM_200HZ;
0199 if (!of_property_read_u32(node, "fdim", &val)) {
0200 switch (val) {
0201 case 100:
0202 pdata->fdim = TPS65217_BL_FDIM_100HZ;
0203 break;
0204
0205 case 200:
0206 pdata->fdim = TPS65217_BL_FDIM_200HZ;
0207 break;
0208
0209 case 500:
0210 pdata->fdim = TPS65217_BL_FDIM_500HZ;
0211 break;
0212
0213 case 1000:
0214 pdata->fdim = TPS65217_BL_FDIM_1000HZ;
0215 break;
0216
0217 default:
0218 dev_err(&pdev->dev,
0219 "invalid 'fdim' value in the device tree\n");
0220 err = ERR_PTR(-EINVAL);
0221 goto err;
0222 }
0223 }
0224
0225 if (!of_property_read_u32(node, "default-brightness", &val)) {
0226 if (val > 100) {
0227 dev_err(&pdev->dev,
0228 "invalid 'default-brightness' value in the device tree\n");
0229 err = ERR_PTR(-EINVAL);
0230 goto err;
0231 }
0232
0233 pdata->dft_brightness = val;
0234 }
0235
0236 of_node_put(node);
0237
0238 return pdata;
0239
0240 err:
0241 of_node_put(node);
0242
0243 return err;
0244 }
0245 #else
0246 static struct tps65217_bl_pdata *
0247 tps65217_bl_parse_dt(struct platform_device *pdev)
0248 {
0249 return NULL;
0250 }
0251 #endif
0252
0253 static int tps65217_bl_probe(struct platform_device *pdev)
0254 {
0255 int rc;
0256 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
0257 struct tps65217_bl *tps65217_bl;
0258 struct tps65217_bl_pdata *pdata;
0259 struct backlight_properties bl_props;
0260
0261 pdata = tps65217_bl_parse_dt(pdev);
0262 if (IS_ERR(pdata))
0263 return PTR_ERR(pdata);
0264
0265 tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
0266 GFP_KERNEL);
0267 if (tps65217_bl == NULL)
0268 return -ENOMEM;
0269
0270 tps65217_bl->tps = tps;
0271 tps65217_bl->dev = &pdev->dev;
0272 tps65217_bl->is_enabled = false;
0273
0274 rc = tps65217_bl_hw_init(tps65217_bl, pdata);
0275 if (rc)
0276 return rc;
0277
0278 memset(&bl_props, 0, sizeof(struct backlight_properties));
0279 bl_props.type = BACKLIGHT_RAW;
0280 bl_props.max_brightness = 100;
0281
0282 tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
0283 tps65217_bl->dev, tps65217_bl,
0284 &tps65217_bl_ops, &bl_props);
0285 if (IS_ERR(tps65217_bl->bl)) {
0286 dev_err(tps65217_bl->dev,
0287 "registration of backlight device failed: %d\n", rc);
0288 return PTR_ERR(tps65217_bl->bl);
0289 }
0290
0291 tps65217_bl->bl->props.brightness = pdata->dft_brightness;
0292 backlight_update_status(tps65217_bl->bl);
0293 platform_set_drvdata(pdev, tps65217_bl);
0294
0295 return 0;
0296 }
0297
0298 #ifdef CONFIG_OF
0299 static const struct of_device_id tps65217_bl_of_match[] = {
0300 { .compatible = "ti,tps65217-bl", },
0301 { },
0302 };
0303 MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
0304 #endif
0305
0306 static struct platform_driver tps65217_bl_driver = {
0307 .probe = tps65217_bl_probe,
0308 .driver = {
0309 .name = "tps65217-bl",
0310 .of_match_table = of_match_ptr(tps65217_bl_of_match),
0311 },
0312 };
0313
0314 module_platform_driver(tps65217_bl_driver);
0315
0316 MODULE_DESCRIPTION("TPS65217 Backlight driver");
0317 MODULE_LICENSE("GPL v2");
0318 MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");