linux/drivers/video/backlight/bd6107.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ROHM Semiconductor BD6107 LED Driver
   4 *
   5 * Copyright (C) 2013 Ideas on board SPRL
   6 *
   7 * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
   8 */
   9
  10#include <linux/backlight.h>
  11#include <linux/delay.h>
  12#include <linux/err.h>
  13#include <linux/fb.h>
  14#include <linux/gpio/consumer.h>
  15#include <linux/i2c.h>
  16#include <linux/module.h>
  17#include <linux/platform_data/bd6107.h>
  18#include <linux/slab.h>
  19
  20#define BD6107_PSCNT1                           0x00
  21#define BD6107_PSCNT1_PSCNTREG2                 (1 << 2)
  22#define BD6107_PSCNT1_PSCNTREG1                 (1 << 0)
  23#define BD6107_REGVSET                          0x02
  24#define BD6107_REGVSET_REG1VSET_2_85V           (1 << 2)
  25#define BD6107_REGVSET_REG1VSET_2_80V           (0 << 2)
  26#define BD6107_LEDCNT1                          0x03
  27#define BD6107_LEDCNT1_LEDONOFF2                (1 << 1)
  28#define BD6107_LEDCNT1_LEDONOFF1                (1 << 0)
  29#define BD6107_PORTSEL                          0x04
  30#define BD6107_PORTSEL_LEDM(n)                  (1 << (n))
  31#define BD6107_RGB1CNT1                         0x05
  32#define BD6107_RGB1CNT2                         0x06
  33#define BD6107_RGB1CNT3                         0x07
  34#define BD6107_RGB1CNT4                         0x08
  35#define BD6107_RGB1CNT5                         0x09
  36#define BD6107_RGB1FLM                          0x0a
  37#define BD6107_RGB2CNT1                         0x0b
  38#define BD6107_RGB2CNT2                         0x0c
  39#define BD6107_RGB2CNT3                         0x0d
  40#define BD6107_RGB2CNT4                         0x0e
  41#define BD6107_RGB2CNT5                         0x0f
  42#define BD6107_RGB2FLM                          0x10
  43#define BD6107_PSCONT3                          0x11
  44#define BD6107_SMMONCNT                         0x12
  45#define BD6107_DCDCCNT                          0x13
  46#define BD6107_IOSEL                            0x14
  47#define BD6107_OUT1                             0x15
  48#define BD6107_OUT2                             0x16
  49#define BD6107_MASK1                            0x17
  50#define BD6107_MASK2                            0x18
  51#define BD6107_FACTOR1                          0x19
  52#define BD6107_FACTOR2                          0x1a
  53#define BD6107_CLRFACT1                         0x1b
  54#define BD6107_CLRFACT2                         0x1c
  55#define BD6107_STATE1                           0x1d
  56#define BD6107_LSIVER                           0x1e
  57#define BD6107_GRPSEL                           0x1f
  58#define BD6107_LEDCNT2                          0x20
  59#define BD6107_LEDCNT3                          0x21
  60#define BD6107_MCURRENT                         0x22
  61#define BD6107_MAINCNT1                         0x23
  62#define BD6107_MAINCNT2                         0x24
  63#define BD6107_SLOPECNT                         0x25
  64#define BD6107_MSLOPE                           0x26
  65#define BD6107_RGBSLOPE                         0x27
  66#define BD6107_TEST                             0x29
  67#define BD6107_SFTRST                           0x2a
  68#define BD6107_SFTRSTGD                         0x2b
  69
  70struct bd6107 {
  71        struct i2c_client *client;
  72        struct backlight_device *backlight;
  73        struct bd6107_platform_data *pdata;
  74        struct gpio_desc *reset;
  75};
  76
  77static int bd6107_write(struct bd6107 *bd, u8 reg, u8 data)
  78{
  79        return i2c_smbus_write_byte_data(bd->client, reg, data);
  80}
  81
  82static int bd6107_backlight_update_status(struct backlight_device *backlight)
  83{
  84        struct bd6107 *bd = bl_get_data(backlight);
  85        int brightness = backlight->props.brightness;
  86
  87        if (backlight->props.power != FB_BLANK_UNBLANK ||
  88            backlight->props.fb_blank != FB_BLANK_UNBLANK ||
  89            backlight->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
  90                brightness = 0;
  91
  92        if (brightness) {
  93                bd6107_write(bd, BD6107_PORTSEL, BD6107_PORTSEL_LEDM(2) |
  94                             BD6107_PORTSEL_LEDM(1) | BD6107_PORTSEL_LEDM(0));
  95                bd6107_write(bd, BD6107_MAINCNT1, brightness);
  96                bd6107_write(bd, BD6107_LEDCNT1, BD6107_LEDCNT1_LEDONOFF1);
  97        } else {
  98                /* Assert the reset line (gpiolib will handle active low) */
  99                gpiod_set_value(bd->reset, 1);
 100                msleep(24);
 101                gpiod_set_value(bd->reset, 0);
 102        }
 103
 104        return 0;
 105}
 106
 107static int bd6107_backlight_check_fb(struct backlight_device *backlight,
 108                                       struct fb_info *info)
 109{
 110        struct bd6107 *bd = bl_get_data(backlight);
 111
 112        return bd->pdata->fbdev == NULL || bd->pdata->fbdev == info->dev;
 113}
 114
 115static const struct backlight_ops bd6107_backlight_ops = {
 116        .options        = BL_CORE_SUSPENDRESUME,
 117        .update_status  = bd6107_backlight_update_status,
 118        .check_fb       = bd6107_backlight_check_fb,
 119};
 120
 121static int bd6107_probe(struct i2c_client *client,
 122                          const struct i2c_device_id *id)
 123{
 124        struct bd6107_platform_data *pdata = dev_get_platdata(&client->dev);
 125        struct backlight_device *backlight;
 126        struct backlight_properties props;
 127        struct bd6107 *bd;
 128        int ret;
 129
 130        if (pdata == NULL) {
 131                dev_err(&client->dev, "No platform data\n");
 132                return -EINVAL;
 133        }
 134
 135        if (!i2c_check_functionality(client->adapter,
 136                                     I2C_FUNC_SMBUS_BYTE_DATA)) {
 137                dev_warn(&client->dev,
 138                         "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
 139                return -EIO;
 140        }
 141
 142        bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL);
 143        if (!bd)
 144                return -ENOMEM;
 145
 146        bd->client = client;
 147        bd->pdata = pdata;
 148
 149        /*
 150         * Request the reset GPIO line with GPIOD_OUT_HIGH meaning asserted,
 151         * so in the machine descriptor table (or other hardware description),
 152         * the line should be flagged as active low so this will assert
 153         * the reset.
 154         */
 155        bd->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
 156        if (IS_ERR(bd->reset)) {
 157                dev_err(&client->dev, "unable to request reset GPIO\n");
 158                ret = PTR_ERR(bd->reset);
 159                return ret;
 160        }
 161
 162        memset(&props, 0, sizeof(props));
 163        props.type = BACKLIGHT_RAW;
 164        props.max_brightness = 128;
 165        props.brightness = clamp_t(unsigned int, pdata->def_value, 0,
 166                                   props.max_brightness);
 167
 168        backlight = devm_backlight_device_register(&client->dev,
 169                                              dev_name(&client->dev),
 170                                              &bd->client->dev, bd,
 171                                              &bd6107_backlight_ops, &props);
 172        if (IS_ERR(backlight)) {
 173                dev_err(&client->dev, "failed to register backlight\n");
 174                return PTR_ERR(backlight);
 175        }
 176
 177        backlight_update_status(backlight);
 178        i2c_set_clientdata(client, backlight);
 179
 180        return 0;
 181}
 182
 183static int bd6107_remove(struct i2c_client *client)
 184{
 185        struct backlight_device *backlight = i2c_get_clientdata(client);
 186
 187        backlight->props.brightness = 0;
 188        backlight_update_status(backlight);
 189
 190        return 0;
 191}
 192
 193static const struct i2c_device_id bd6107_ids[] = {
 194        { "bd6107", 0 },
 195        { }
 196};
 197MODULE_DEVICE_TABLE(i2c, bd6107_ids);
 198
 199static struct i2c_driver bd6107_driver = {
 200        .driver = {
 201                .name = "bd6107",
 202        },
 203        .probe = bd6107_probe,
 204        .remove = bd6107_remove,
 205        .id_table = bd6107_ids,
 206};
 207
 208module_i2c_driver(bd6107_driver);
 209
 210MODULE_DESCRIPTION("Rohm BD6107 Backlight Driver");
 211MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 212MODULE_LICENSE("GPL");
 213