linux/drivers/leds/leds-lp55xx-common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * LP5521/LP5523/LP55231/LP5562 Common Driver
   4 *
   5 * Copyright 2012 Texas Instruments
   6 *
   7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
   8 *
   9 * Derived from leds-lp5521.c, leds-lp5523.c
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/delay.h>
  14#include <linux/firmware.h>
  15#include <linux/i2c.h>
  16#include <linux/leds.h>
  17#include <linux/module.h>
  18#include <linux/platform_data/leds-lp55xx.h>
  19#include <linux/slab.h>
  20#include <linux/gpio/consumer.h>
  21
  22#include "leds-lp55xx-common.h"
  23
  24/* External clock rate */
  25#define LP55XX_CLK_32K                  32768
  26
  27static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
  28{
  29        return container_of(cdev, struct lp55xx_led, cdev);
  30}
  31
  32static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
  33{
  34        return cdev_to_lp55xx_led(dev_get_drvdata(dev));
  35}
  36
  37static struct lp55xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
  38{
  39        return container_of(mc_cdev, struct lp55xx_led, mc_cdev);
  40}
  41
  42static void lp55xx_reset_device(struct lp55xx_chip *chip)
  43{
  44        struct lp55xx_device_config *cfg = chip->cfg;
  45        u8 addr = cfg->reset.addr;
  46        u8 val  = cfg->reset.val;
  47
  48        /* no error checking here because no ACK from the device after reset */
  49        lp55xx_write(chip, addr, val);
  50}
  51
  52static int lp55xx_detect_device(struct lp55xx_chip *chip)
  53{
  54        struct lp55xx_device_config *cfg = chip->cfg;
  55        u8 addr = cfg->enable.addr;
  56        u8 val  = cfg->enable.val;
  57        int ret;
  58
  59        ret = lp55xx_write(chip, addr, val);
  60        if (ret)
  61                return ret;
  62
  63        usleep_range(1000, 2000);
  64
  65        ret = lp55xx_read(chip, addr, &val);
  66        if (ret)
  67                return ret;
  68
  69        if (val != cfg->enable.val)
  70                return -ENODEV;
  71
  72        return 0;
  73}
  74
  75static int lp55xx_post_init_device(struct lp55xx_chip *chip)
  76{
  77        struct lp55xx_device_config *cfg = chip->cfg;
  78
  79        if (!cfg->post_init_device)
  80                return 0;
  81
  82        return cfg->post_init_device(chip);
  83}
  84
  85static ssize_t led_current_show(struct device *dev,
  86                            struct device_attribute *attr,
  87                            char *buf)
  88{
  89        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
  90
  91        return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_current);
  92}
  93
  94static ssize_t led_current_store(struct device *dev,
  95                             struct device_attribute *attr,
  96                             const char *buf, size_t len)
  97{
  98        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
  99        struct lp55xx_chip *chip = led->chip;
 100        unsigned long curr;
 101
 102        if (kstrtoul(buf, 0, &curr))
 103                return -EINVAL;
 104
 105        if (curr > led->max_current)
 106                return -EINVAL;
 107
 108        if (!chip->cfg->set_led_current)
 109                return len;
 110
 111        mutex_lock(&chip->lock);
 112        chip->cfg->set_led_current(led, (u8)curr);
 113        mutex_unlock(&chip->lock);
 114
 115        return len;
 116}
 117
 118static ssize_t max_current_show(struct device *dev,
 119                            struct device_attribute *attr,
 120                            char *buf)
 121{
 122        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
 123
 124        return scnprintf(buf, PAGE_SIZE, "%d\n", led->max_current);
 125}
 126
 127static DEVICE_ATTR_RW(led_current);
 128static DEVICE_ATTR_RO(max_current);
 129
 130static struct attribute *lp55xx_led_attrs[] = {
 131        &dev_attr_led_current.attr,
 132        &dev_attr_max_current.attr,
 133        NULL,
 134};
 135ATTRIBUTE_GROUPS(lp55xx_led);
 136
 137static int lp55xx_set_mc_brightness(struct led_classdev *cdev,
 138                                    enum led_brightness brightness)
 139{
 140        struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
 141        struct lp55xx_led *led = mcled_cdev_to_led(mc_dev);
 142        struct lp55xx_device_config *cfg = led->chip->cfg;
 143
 144        led_mc_calc_color_components(&led->mc_cdev, brightness);
 145        return cfg->multicolor_brightness_fn(led);
 146
 147}
 148
 149static int lp55xx_set_brightness(struct led_classdev *cdev,
 150                             enum led_brightness brightness)
 151{
 152        struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
 153        struct lp55xx_device_config *cfg = led->chip->cfg;
 154
 155        led->brightness = (u8)brightness;
 156        return cfg->brightness_fn(led);
 157}
 158
 159static int lp55xx_init_led(struct lp55xx_led *led,
 160                        struct lp55xx_chip *chip, int chan)
 161{
 162        struct lp55xx_platform_data *pdata = chip->pdata;
 163        struct lp55xx_device_config *cfg = chip->cfg;
 164        struct device *dev = &chip->cl->dev;
 165        int max_channel = cfg->max_channel;
 166        struct mc_subled *mc_led_info;
 167        struct led_classdev *led_cdev;
 168        char name[32];
 169        int i, j = 0;
 170        int ret;
 171
 172        if (chan >= max_channel) {
 173                dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
 174                return -EINVAL;
 175        }
 176
 177        if (pdata->led_config[chan].led_current == 0)
 178                return 0;
 179
 180        if (pdata->led_config[chan].name) {
 181                led->cdev.name = pdata->led_config[chan].name;
 182        } else {
 183                snprintf(name, sizeof(name), "%s:channel%d",
 184                        pdata->label ? : chip->cl->name, chan);
 185                led->cdev.name = name;
 186        }
 187
 188        if (pdata->led_config[chan].num_colors > 1) {
 189                mc_led_info = devm_kcalloc(dev,
 190                                           pdata->led_config[chan].num_colors,
 191                                           sizeof(*mc_led_info), GFP_KERNEL);
 192                if (!mc_led_info)
 193                        return -ENOMEM;
 194
 195                led_cdev = &led->mc_cdev.led_cdev;
 196                led_cdev->name = led->cdev.name;
 197                led_cdev->brightness_set_blocking = lp55xx_set_mc_brightness;
 198                led->mc_cdev.num_colors = pdata->led_config[chan].num_colors;
 199                for (i = 0; i < led->mc_cdev.num_colors; i++) {
 200                        mc_led_info[i].color_index =
 201                                pdata->led_config[chan].color_id[i];
 202                        mc_led_info[i].channel =
 203                                        pdata->led_config[chan].output_num[i];
 204                        j++;
 205                }
 206
 207                led->mc_cdev.subled_info = mc_led_info;
 208        } else {
 209                led->cdev.brightness_set_blocking = lp55xx_set_brightness;
 210        }
 211
 212        led->cdev.groups = lp55xx_led_groups;
 213        led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
 214        led->led_current = pdata->led_config[chan].led_current;
 215        led->max_current = pdata->led_config[chan].max_current;
 216        led->chan_nr = pdata->led_config[chan].chan_nr;
 217
 218        if (led->chan_nr >= max_channel) {
 219                dev_err(dev, "Use channel numbers between 0 and %d\n",
 220                        max_channel - 1);
 221                return -EINVAL;
 222        }
 223
 224        if (pdata->led_config[chan].num_colors > 1)
 225                ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
 226        else
 227                ret = devm_led_classdev_register(dev, &led->cdev);
 228
 229        if (ret) {
 230                dev_err(dev, "led register err: %d\n", ret);
 231                return ret;
 232        }
 233
 234        return 0;
 235}
 236
 237static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
 238{
 239        struct lp55xx_chip *chip = context;
 240        struct device *dev = &chip->cl->dev;
 241        enum lp55xx_engine_index idx = chip->engine_idx;
 242
 243        if (!fw) {
 244                dev_err(dev, "firmware request failed\n");
 245                return;
 246        }
 247
 248        /* handling firmware data is chip dependent */
 249        mutex_lock(&chip->lock);
 250
 251        chip->engines[idx - 1].mode = LP55XX_ENGINE_LOAD;
 252        chip->fw = fw;
 253        if (chip->cfg->firmware_cb)
 254                chip->cfg->firmware_cb(chip);
 255
 256        mutex_unlock(&chip->lock);
 257
 258        /* firmware should be released for other channel use */
 259        release_firmware(chip->fw);
 260        chip->fw = NULL;
 261}
 262
 263static int lp55xx_request_firmware(struct lp55xx_chip *chip)
 264{
 265        const char *name = chip->cl->name;
 266        struct device *dev = &chip->cl->dev;
 267
 268        return request_firmware_nowait(THIS_MODULE, false, name, dev,
 269                                GFP_KERNEL, chip, lp55xx_firmware_loaded);
 270}
 271
 272static ssize_t select_engine_show(struct device *dev,
 273                            struct device_attribute *attr,
 274                            char *buf)
 275{
 276        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 277        struct lp55xx_chip *chip = led->chip;
 278
 279        return sprintf(buf, "%d\n", chip->engine_idx);
 280}
 281
 282static ssize_t select_engine_store(struct device *dev,
 283                             struct device_attribute *attr,
 284                             const char *buf, size_t len)
 285{
 286        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 287        struct lp55xx_chip *chip = led->chip;
 288        unsigned long val;
 289        int ret;
 290
 291        if (kstrtoul(buf, 0, &val))
 292                return -EINVAL;
 293
 294        /* select the engine to be run */
 295
 296        switch (val) {
 297        case LP55XX_ENGINE_1:
 298        case LP55XX_ENGINE_2:
 299        case LP55XX_ENGINE_3:
 300                mutex_lock(&chip->lock);
 301                chip->engine_idx = val;
 302                ret = lp55xx_request_firmware(chip);
 303                mutex_unlock(&chip->lock);
 304                break;
 305        default:
 306                dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
 307                return -EINVAL;
 308        }
 309
 310        if (ret) {
 311                dev_err(dev, "request firmware err: %d\n", ret);
 312                return ret;
 313        }
 314
 315        return len;
 316}
 317
 318static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
 319{
 320        if (chip->cfg->run_engine)
 321                chip->cfg->run_engine(chip, start);
 322}
 323
 324static ssize_t run_engine_store(struct device *dev,
 325                             struct device_attribute *attr,
 326                             const char *buf, size_t len)
 327{
 328        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 329        struct lp55xx_chip *chip = led->chip;
 330        unsigned long val;
 331
 332        if (kstrtoul(buf, 0, &val))
 333                return -EINVAL;
 334
 335        /* run or stop the selected engine */
 336
 337        if (val <= 0) {
 338                lp55xx_run_engine(chip, false);
 339                return len;
 340        }
 341
 342        mutex_lock(&chip->lock);
 343        lp55xx_run_engine(chip, true);
 344        mutex_unlock(&chip->lock);
 345
 346        return len;
 347}
 348
 349static DEVICE_ATTR_RW(select_engine);
 350static DEVICE_ATTR_WO(run_engine);
 351
 352static struct attribute *lp55xx_engine_attributes[] = {
 353        &dev_attr_select_engine.attr,
 354        &dev_attr_run_engine.attr,
 355        NULL,
 356};
 357
 358static const struct attribute_group lp55xx_engine_attr_group = {
 359        .attrs = lp55xx_engine_attributes,
 360};
 361
 362int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
 363{
 364        return i2c_smbus_write_byte_data(chip->cl, reg, val);
 365}
 366EXPORT_SYMBOL_GPL(lp55xx_write);
 367
 368int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
 369{
 370        s32 ret;
 371
 372        ret = i2c_smbus_read_byte_data(chip->cl, reg);
 373        if (ret < 0)
 374                return ret;
 375
 376        *val = ret;
 377        return 0;
 378}
 379EXPORT_SYMBOL_GPL(lp55xx_read);
 380
 381int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
 382{
 383        int ret;
 384        u8 tmp;
 385
 386        ret = lp55xx_read(chip, reg, &tmp);
 387        if (ret)
 388                return ret;
 389
 390        tmp &= ~mask;
 391        tmp |= val & mask;
 392
 393        return lp55xx_write(chip, reg, tmp);
 394}
 395EXPORT_SYMBOL_GPL(lp55xx_update_bits);
 396
 397bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
 398{
 399        struct clk *clk;
 400        int err;
 401
 402        clk = devm_clk_get(&chip->cl->dev, "32k_clk");
 403        if (IS_ERR(clk))
 404                goto use_internal_clk;
 405
 406        err = clk_prepare_enable(clk);
 407        if (err)
 408                goto use_internal_clk;
 409
 410        if (clk_get_rate(clk) != LP55XX_CLK_32K) {
 411                clk_disable_unprepare(clk);
 412                goto use_internal_clk;
 413        }
 414
 415        dev_info(&chip->cl->dev, "%dHz external clock used\n",  LP55XX_CLK_32K);
 416
 417        chip->clk = clk;
 418        return true;
 419
 420use_internal_clk:
 421        dev_info(&chip->cl->dev, "internal clock used\n");
 422        return false;
 423}
 424EXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
 425
 426int lp55xx_init_device(struct lp55xx_chip *chip)
 427{
 428        struct lp55xx_platform_data *pdata;
 429        struct lp55xx_device_config *cfg;
 430        struct device *dev = &chip->cl->dev;
 431        int ret = 0;
 432
 433        WARN_ON(!chip);
 434
 435        pdata = chip->pdata;
 436        cfg = chip->cfg;
 437
 438        if (!pdata || !cfg)
 439                return -EINVAL;
 440
 441        if (pdata->enable_gpiod) {
 442                gpiod_set_consumer_name(pdata->enable_gpiod, "LP55xx enable");
 443                gpiod_set_value(pdata->enable_gpiod, 0);
 444                usleep_range(1000, 2000); /* Keep enable down at least 1ms */
 445                gpiod_set_value(pdata->enable_gpiod, 1);
 446                usleep_range(1000, 2000); /* 500us abs min. */
 447        }
 448
 449        lp55xx_reset_device(chip);
 450
 451        /*
 452         * Exact value is not available. 10 - 20ms
 453         * appears to be enough for reset.
 454         */
 455        usleep_range(10000, 20000);
 456
 457        ret = lp55xx_detect_device(chip);
 458        if (ret) {
 459                dev_err(dev, "device detection err: %d\n", ret);
 460                goto err;
 461        }
 462
 463        /* chip specific initialization */
 464        ret = lp55xx_post_init_device(chip);
 465        if (ret) {
 466                dev_err(dev, "post init device err: %d\n", ret);
 467                goto err_post_init;
 468        }
 469
 470        return 0;
 471
 472err_post_init:
 473        lp55xx_deinit_device(chip);
 474err:
 475        return ret;
 476}
 477EXPORT_SYMBOL_GPL(lp55xx_init_device);
 478
 479void lp55xx_deinit_device(struct lp55xx_chip *chip)
 480{
 481        struct lp55xx_platform_data *pdata = chip->pdata;
 482
 483        if (chip->clk)
 484                clk_disable_unprepare(chip->clk);
 485
 486        if (pdata->enable_gpiod)
 487                gpiod_set_value(pdata->enable_gpiod, 0);
 488}
 489EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
 490
 491int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
 492{
 493        struct lp55xx_platform_data *pdata = chip->pdata;
 494        struct lp55xx_device_config *cfg = chip->cfg;
 495        int num_channels = pdata->num_channels;
 496        struct lp55xx_led *each;
 497        u8 led_current;
 498        int ret;
 499        int i;
 500
 501        if (!cfg->brightness_fn) {
 502                dev_err(&chip->cl->dev, "empty brightness configuration\n");
 503                return -EINVAL;
 504        }
 505
 506        for (i = 0; i < num_channels; i++) {
 507
 508                /* do not initialize channels that are not connected */
 509                if (pdata->led_config[i].led_current == 0)
 510                        continue;
 511
 512                led_current = pdata->led_config[i].led_current;
 513                each = led + i;
 514                ret = lp55xx_init_led(each, chip, i);
 515                if (ret)
 516                        goto err_init_led;
 517
 518                chip->num_leds++;
 519                each->chip = chip;
 520
 521                /* setting led current at each channel */
 522                if (cfg->set_led_current)
 523                        cfg->set_led_current(each, led_current);
 524        }
 525
 526        return 0;
 527
 528err_init_led:
 529        return ret;
 530}
 531EXPORT_SYMBOL_GPL(lp55xx_register_leds);
 532
 533int lp55xx_register_sysfs(struct lp55xx_chip *chip)
 534{
 535        struct device *dev = &chip->cl->dev;
 536        struct lp55xx_device_config *cfg = chip->cfg;
 537        int ret;
 538
 539        if (!cfg->run_engine || !cfg->firmware_cb)
 540                goto dev_specific_attrs;
 541
 542        ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
 543        if (ret)
 544                return ret;
 545
 546dev_specific_attrs:
 547        return cfg->dev_attr_group ?
 548                sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
 549}
 550EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
 551
 552void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
 553{
 554        struct device *dev = &chip->cl->dev;
 555        struct lp55xx_device_config *cfg = chip->cfg;
 556
 557        if (cfg->dev_attr_group)
 558                sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
 559
 560        sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
 561}
 562EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
 563
 564static int lp55xx_parse_common_child(struct device_node *np,
 565                                     struct lp55xx_led_config *cfg,
 566                                     int led_number, int *chan_nr)
 567{
 568        int ret;
 569
 570        of_property_read_string(np, "chan-name",
 571                                &cfg[led_number].name);
 572        of_property_read_u8(np, "led-cur",
 573                            &cfg[led_number].led_current);
 574        of_property_read_u8(np, "max-cur",
 575                            &cfg[led_number].max_current);
 576
 577        ret = of_property_read_u32(np, "reg", chan_nr);
 578        if (ret)
 579                return ret;
 580
 581        if (*chan_nr < 0 || *chan_nr > cfg->max_channel)
 582                return -EINVAL;
 583
 584        return 0;
 585}
 586
 587static int lp55xx_parse_multi_led_child(struct device_node *child,
 588                                         struct lp55xx_led_config *cfg,
 589                                         int child_number, int color_number)
 590{
 591        int chan_nr, color_id, ret;
 592
 593        ret = lp55xx_parse_common_child(child, cfg, child_number, &chan_nr);
 594        if (ret)
 595                return ret;
 596
 597        ret = of_property_read_u32(child, "color", &color_id);
 598        if (ret)
 599                return ret;
 600
 601        cfg[child_number].color_id[color_number] = color_id;
 602        cfg[child_number].output_num[color_number] = chan_nr;
 603
 604        return 0;
 605}
 606
 607static int lp55xx_parse_multi_led(struct device_node *np,
 608                                  struct lp55xx_led_config *cfg,
 609                                  int child_number)
 610{
 611        struct device_node *child;
 612        int num_colors = 0, ret;
 613
 614        for_each_available_child_of_node(np, child) {
 615                ret = lp55xx_parse_multi_led_child(child, cfg, child_number,
 616                                                   num_colors);
 617                if (ret) {
 618                        of_node_put(child);
 619                        return ret;
 620                }
 621                num_colors++;
 622        }
 623
 624        cfg[child_number].num_colors = num_colors;
 625
 626        return 0;
 627}
 628
 629static int lp55xx_parse_logical_led(struct device_node *np,
 630                                   struct lp55xx_led_config *cfg,
 631                                   int child_number)
 632{
 633        int led_color, ret;
 634        int chan_nr = 0;
 635
 636        cfg[child_number].default_trigger =
 637                of_get_property(np, "linux,default-trigger", NULL);
 638
 639        ret = of_property_read_u32(np, "color", &led_color);
 640        if (ret)
 641                return ret;
 642
 643        if (led_color == LED_COLOR_ID_RGB)
 644                return lp55xx_parse_multi_led(np, cfg, child_number);
 645
 646        ret =  lp55xx_parse_common_child(np, cfg, child_number, &chan_nr);
 647        if (ret < 0)
 648                return ret;
 649
 650        cfg[child_number].chan_nr = chan_nr;
 651
 652        return ret;
 653}
 654
 655struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
 656                                                      struct device_node *np,
 657                                                      struct lp55xx_chip *chip)
 658{
 659        struct device_node *child;
 660        struct lp55xx_platform_data *pdata;
 661        struct lp55xx_led_config *cfg;
 662        int num_channels;
 663        int i = 0;
 664        int ret;
 665
 666        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 667        if (!pdata)
 668                return ERR_PTR(-ENOMEM);
 669
 670        num_channels = of_get_available_child_count(np);
 671        if (num_channels == 0) {
 672                dev_err(dev, "no LED channels\n");
 673                return ERR_PTR(-EINVAL);
 674        }
 675
 676        cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL);
 677        if (!cfg)
 678                return ERR_PTR(-ENOMEM);
 679
 680        pdata->led_config = &cfg[0];
 681        pdata->num_channels = num_channels;
 682        cfg->max_channel = chip->cfg->max_channel;
 683
 684        for_each_available_child_of_node(np, child) {
 685                ret = lp55xx_parse_logical_led(child, cfg, i);
 686                if (ret) {
 687                        of_node_put(child);
 688                        return ERR_PTR(-EINVAL);
 689                }
 690                i++;
 691        }
 692
 693        of_property_read_string(np, "label", &pdata->label);
 694        of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
 695
 696        pdata->enable_gpiod = devm_gpiod_get_optional(dev, "enable",
 697                                                      GPIOD_OUT_LOW);
 698        if (IS_ERR(pdata->enable_gpiod))
 699                return ERR_CAST(pdata->enable_gpiod);
 700
 701        /* LP8501 specific */
 702        of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
 703
 704        return pdata;
 705}
 706EXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
 707
 708MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
 709MODULE_DESCRIPTION("LP55xx Common Driver");
 710MODULE_LICENSE("GPL");
 711