Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * tps65217_bl.c
0004  *
0005  * TPS65217 backlight driver
0006  *
0007  * Copyright (C) 2012 Matthias Kaehlcke
0008  * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
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         /* select ISET_1 current level */
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         /* select ISET2 current level */
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     /* set PWM frequency */
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     { /* sentinel */ },
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>");