LCOV - code coverage report
Current view: top level - drivers/gpu/drm/display - drm_dp_dual_mode_helper.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 118 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 9 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright © 2016 Intel Corporation
       3             :  *
       4             :  * Permission is hereby granted, free of charge, to any person obtaining a
       5             :  * copy of this software and associated documentation files (the "Software"),
       6             :  * to deal in the Software without restriction, including without limitation
       7             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
       8             :  * and/or sell copies of the Software, and to permit persons to whom the
       9             :  * Software is furnished to do so, subject to the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included in
      12             :  * all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      17             :  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
      18             :  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      19             :  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      20             :  * OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #include <linux/delay.h>
      24             : #include <linux/errno.h>
      25             : #include <linux/export.h>
      26             : #include <linux/i2c.h>
      27             : #include <linux/slab.h>
      28             : #include <linux/string.h>
      29             : 
      30             : #include <drm/display/drm_dp_dual_mode_helper.h>
      31             : #include <drm/drm_device.h>
      32             : #include <drm/drm_print.h>
      33             : 
      34             : /**
      35             :  * DOC: dp dual mode helpers
      36             :  *
      37             :  * Helper functions to deal with DP dual mode (aka. DP++) adaptors.
      38             :  *
      39             :  * Type 1:
      40             :  * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C.
      41             :  *
      42             :  * Type 2:
      43             :  * Adaptor registers and sink DDC bus can be accessed either via I2C or
      44             :  * I2C-over-AUX. Source devices may choose to implement either of these
      45             :  * access methods.
      46             :  */
      47             : 
      48             : #define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
      49             : 
      50             : /**
      51             :  * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s)
      52             :  * @adapter: I2C adapter for the DDC bus
      53             :  * @offset: register offset
      54             :  * @buffer: buffer for return data
      55             :  * @size: sizo of the buffer
      56             :  *
      57             :  * Reads @size bytes from the DP dual mode adaptor registers
      58             :  * starting at @offset.
      59             :  *
      60             :  * Returns:
      61             :  * 0 on success, negative error code on failure
      62             :  */
      63           0 : ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
      64             :                               u8 offset, void *buffer, size_t size)
      65             : {
      66           0 :         struct i2c_msg msgs[] = {
      67             :                 {
      68             :                         .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
      69             :                         .flags = 0,
      70             :                         .len = 1,
      71             :                         .buf = &offset,
      72             :                 },
      73             :                 {
      74             :                         .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
      75             :                         .flags = I2C_M_RD,
      76             :                         .len = size,
      77             :                         .buf = buffer,
      78             :                 },
      79             :         };
      80             :         int ret;
      81             : 
      82           0 :         ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
      83           0 :         if (ret < 0)
      84           0 :                 return ret;
      85           0 :         if (ret != ARRAY_SIZE(msgs))
      86             :                 return -EPROTO;
      87             : 
      88           0 :         return 0;
      89             : }
      90             : EXPORT_SYMBOL(drm_dp_dual_mode_read);
      91             : 
      92             : /**
      93             :  * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
      94             :  * @adapter: I2C adapter for the DDC bus
      95             :  * @offset: register offset
      96             :  * @buffer: buffer for write data
      97             :  * @size: sizo of the buffer
      98             :  *
      99             :  * Writes @size bytes to the DP dual mode adaptor registers
     100             :  * starting at @offset.
     101             :  *
     102             :  * Returns:
     103             :  * 0 on success, negative error code on failure
     104             :  */
     105           0 : ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
     106             :                                u8 offset, const void *buffer, size_t size)
     107             : {
     108           0 :         struct i2c_msg msg = {
     109             :                 .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
     110             :                 .flags = 0,
     111           0 :                 .len = 1 + size,
     112             :                 .buf = NULL,
     113             :         };
     114             :         void *data;
     115             :         int ret;
     116             : 
     117           0 :         data = kmalloc(msg.len, GFP_KERNEL);
     118           0 :         if (!data)
     119             :                 return -ENOMEM;
     120             : 
     121           0 :         msg.buf = data;
     122             : 
     123           0 :         memcpy(data, &offset, 1);
     124           0 :         memcpy(data + 1, buffer, size);
     125             : 
     126           0 :         ret = i2c_transfer(adapter, &msg, 1);
     127             : 
     128           0 :         kfree(data);
     129             : 
     130           0 :         if (ret < 0)
     131           0 :                 return ret;
     132           0 :         if (ret != 1)
     133             :                 return -EPROTO;
     134             : 
     135           0 :         return 0;
     136             : }
     137             : EXPORT_SYMBOL(drm_dp_dual_mode_write);
     138             : 
     139             : static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
     140             : {
     141             :         static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
     142             :                 "DP-HDMI ADAPTOR\x04";
     143             : 
     144           0 :         return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
     145             :                       sizeof(dp_dual_mode_hdmi_id)) == 0;
     146             : }
     147             : 
     148             : static bool is_type1_adaptor(uint8_t adaptor_id)
     149             : {
     150           0 :         return adaptor_id == 0 || adaptor_id == 0xff;
     151             : }
     152             : 
     153             : static bool is_type2_adaptor(uint8_t adaptor_id)
     154             : {
     155             :         return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
     156             :                               DP_DUAL_MODE_REV_TYPE2);
     157             : }
     158             : 
     159             : static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
     160             :                               const uint8_t adaptor_id)
     161             : {
     162           0 :         return is_hdmi_adaptor(hdmi_id) &&
     163             :                 (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
     164             :                  DP_DUAL_MODE_TYPE_HAS_DPCD));
     165             : }
     166             : 
     167             : /**
     168             :  * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
     169             :  * @dev: &drm_device to use
     170             :  * @adapter: I2C adapter for the DDC bus
     171             :  *
     172             :  * Attempt to identify the type of the DP dual mode adaptor used.
     173             :  *
     174             :  * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
     175             :  * certain whether we're dealing with a native HDMI port or
     176             :  * a type 1 DVI dual mode adaptor. The driver will have to use
     177             :  * some other hardware/driver specific mechanism to make that
     178             :  * distinction.
     179             :  *
     180             :  * Returns:
     181             :  * The type of the DP dual mode adaptor used
     182             :  */
     183           0 : enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
     184             :                                                    struct i2c_adapter *adapter)
     185             : {
     186           0 :         char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
     187           0 :         uint8_t adaptor_id = 0x00;
     188             :         ssize_t ret;
     189             : 
     190             :         /*
     191             :          * Let's see if the adaptor is there the by reading the
     192             :          * HDMI ID registers.
     193             :          *
     194             :          * Note that type 1 DVI adaptors are not required to implemnt
     195             :          * any registers, and that presents a problem for detection.
     196             :          * If the i2c transfer is nacked, we may or may not be dealing
     197             :          * with a type 1 DVI adaptor. Some other mechanism of detecting
     198             :          * the presence of the adaptor is required. One way would be
     199             :          * to check the state of the CONFIG1 pin, Another method would
     200             :          * simply require the driver to know whether the port is a DP++
     201             :          * port or a native HDMI port. Both of these methods are entirely
     202             :          * hardware/driver specific so we can't deal with them here.
     203             :          */
     204           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
     205             :                                     hdmi_id, sizeof(hdmi_id));
     206           0 :         drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
     207             :                     ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
     208           0 :         if (ret)
     209             :                 return DRM_DP_DUAL_MODE_UNKNOWN;
     210             : 
     211             :         /*
     212             :          * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
     213             :          * the offset but ignore it, and instead they just always return
     214             :          * data from the start of the HDMI ID buffer. So for a broken
     215             :          * type 1 HDMI adaptor a single byte read will always give us
     216             :          * 0x44, and for a type 1 DVI adaptor it should give 0x00
     217             :          * (assuming it implements any registers). Fortunately neither
     218             :          * of those values will match the type 2 signature of the
     219             :          * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
     220             :          * the type 2 adaptor detection safely even in the presence
     221             :          * of broken type 1 adaptors.
     222             :          */
     223           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
     224             :                                     &adaptor_id, sizeof(adaptor_id));
     225           0 :         drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
     226           0 :         if (ret == 0) {
     227           0 :                 if (is_lspcon_adaptor(hdmi_id, adaptor_id))
     228             :                         return DRM_DP_DUAL_MODE_LSPCON;
     229           0 :                 if (is_type2_adaptor(adaptor_id)) {
     230           0 :                         if (is_hdmi_adaptor(hdmi_id))
     231             :                                 return DRM_DP_DUAL_MODE_TYPE2_HDMI;
     232             :                         else
     233           0 :                                 return DRM_DP_DUAL_MODE_TYPE2_DVI;
     234             :                 }
     235             :                 /*
     236             :                  * If neither a proper type 1 ID nor a broken type 1 adaptor
     237             :                  * as described above, assume type 1, but let the user know
     238             :                  * that we may have misdetected the type.
     239             :                  */
     240           0 :                 if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
     241           0 :                         drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
     242             : 
     243             :         }
     244             : 
     245           0 :         if (is_hdmi_adaptor(hdmi_id))
     246             :                 return DRM_DP_DUAL_MODE_TYPE1_HDMI;
     247             :         else
     248           0 :                 return DRM_DP_DUAL_MODE_TYPE1_DVI;
     249             : }
     250             : EXPORT_SYMBOL(drm_dp_dual_mode_detect);
     251             : 
     252             : /**
     253             :  * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
     254             :  * @dev: &drm_device to use
     255             :  * @type: DP dual mode adaptor type
     256             :  * @adapter: I2C adapter for the DDC bus
     257             :  *
     258             :  * Determine the max TMDS clock the adaptor supports based on the
     259             :  * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
     260             :  * register (on type2 adaptors). As some type 1 adaptors have
     261             :  * problems with registers (see comments in drm_dp_dual_mode_detect())
     262             :  * we don't read the register on those, instead we simply assume
     263             :  * a 165 MHz limit based on the specification.
     264             :  *
     265             :  * Returns:
     266             :  * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
     267             :  */
     268           0 : int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
     269             :                                     struct i2c_adapter *adapter)
     270             : {
     271             :         uint8_t max_tmds_clock;
     272             :         ssize_t ret;
     273             : 
     274             :         /* native HDMI so no limit */
     275           0 :         if (type == DRM_DP_DUAL_MODE_NONE)
     276             :                 return 0;
     277             : 
     278             :         /*
     279             :          * Type 1 adaptors are limited to 165MHz
     280             :          * Type 2 adaptors can tells us their limit
     281             :          */
     282           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
     283             :                 return 165000;
     284             : 
     285           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
     286             :                                     &max_tmds_clock, sizeof(max_tmds_clock));
     287           0 :         if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
     288           0 :                 drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
     289           0 :                 return 165000;
     290             :         }
     291             : 
     292           0 :         return max_tmds_clock * 5000 / 2;
     293             : }
     294             : EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
     295             : 
     296             : /**
     297             :  * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
     298             :  * @dev: &drm_device to use
     299             :  * @type: DP dual mode adaptor type
     300             :  * @adapter: I2C adapter for the DDC bus
     301             :  * @enabled: current state of the TMDS output buffers
     302             :  *
     303             :  * Get the state of the TMDS output buffers in the adaptor. For
     304             :  * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
     305             :  * register. As some type 1 adaptors have problems with registers
     306             :  * (see comments in drm_dp_dual_mode_detect()) we don't read the
     307             :  * register on those, instead we simply assume that the buffers
     308             :  * are always enabled.
     309             :  *
     310             :  * Returns:
     311             :  * 0 on success, negative error code on failure
     312             :  */
     313           0 : int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
     314             :                                      enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
     315             :                                      bool *enabled)
     316             : {
     317             :         uint8_t tmds_oen;
     318             :         ssize_t ret;
     319             : 
     320           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
     321           0 :                 *enabled = true;
     322           0 :                 return 0;
     323             :         }
     324             : 
     325           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
     326             :                                     &tmds_oen, sizeof(tmds_oen));
     327           0 :         if (ret) {
     328           0 :                 drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
     329           0 :                 return ret;
     330             :         }
     331             : 
     332           0 :         *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
     333             : 
     334           0 :         return 0;
     335             : }
     336             : EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
     337             : 
     338             : /**
     339             :  * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
     340             :  * @dev: &drm_device to use
     341             :  * @type: DP dual mode adaptor type
     342             :  * @adapter: I2C adapter for the DDC bus
     343             :  * @enable: enable (as opposed to disable) the TMDS output buffers
     344             :  *
     345             :  * Set the state of the TMDS output buffers in the adaptor. For
     346             :  * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
     347             :  * some type 1 adaptors have problems with registers (see comments
     348             :  * in drm_dp_dual_mode_detect()) we avoid touching the register,
     349             :  * making this function a no-op on type 1 adaptors.
     350             :  *
     351             :  * Returns:
     352             :  * 0 on success, negative error code on failure
     353             :  */
     354           0 : int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
     355             :                                      struct i2c_adapter *adapter, bool enable)
     356             : {
     357           0 :         uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
     358             :         ssize_t ret;
     359             :         int retry;
     360             : 
     361           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
     362             :                 return 0;
     363             : 
     364             :         /*
     365             :          * LSPCON adapters in low-power state may ignore the first write, so
     366             :          * read back and verify the written value a few times.
     367             :          */
     368           0 :         for (retry = 0; retry < 3; retry++) {
     369             :                 uint8_t tmp;
     370             : 
     371           0 :                 ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
     372             :                                              &tmds_oen, sizeof(tmds_oen));
     373           0 :                 if (ret) {
     374           0 :                         drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
     375             :                                     enable ? "enable" : "disable", retry + 1);
     376           0 :                         return ret;
     377             :                 }
     378             : 
     379           0 :                 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
     380             :                                             &tmp, sizeof(tmp));
     381           0 :                 if (ret) {
     382           0 :                         drm_dbg_kms(dev,
     383             :                                     "I2C read failed during TMDS output buffer %s (%d attempts)\n",
     384             :                                     enable ? "enabling" : "disabling", retry + 1);
     385           0 :                         return ret;
     386             :                 }
     387             : 
     388           0 :                 if (tmp == tmds_oen)
     389             :                         return 0;
     390             :         }
     391             : 
     392           0 :         drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
     393             :                     enable ? "enabling" : "disabling");
     394             : 
     395           0 :         return -EIO;
     396             : }
     397             : EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
     398             : 
     399             : /**
     400             :  * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
     401             :  * @type: DP dual mode adaptor type
     402             :  *
     403             :  * Returns:
     404             :  * String representation of the DP dual mode adaptor type
     405             :  */
     406           0 : const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
     407             : {
     408           0 :         switch (type) {
     409             :         case DRM_DP_DUAL_MODE_NONE:
     410             :                 return "none";
     411             :         case DRM_DP_DUAL_MODE_TYPE1_DVI:
     412           0 :                 return "type 1 DVI";
     413             :         case DRM_DP_DUAL_MODE_TYPE1_HDMI:
     414           0 :                 return "type 1 HDMI";
     415             :         case DRM_DP_DUAL_MODE_TYPE2_DVI:
     416           0 :                 return "type 2 DVI";
     417             :         case DRM_DP_DUAL_MODE_TYPE2_HDMI:
     418           0 :                 return "type 2 HDMI";
     419             :         case DRM_DP_DUAL_MODE_LSPCON:
     420           0 :                 return "lspcon";
     421             :         default:
     422           0 :                 WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
     423             :                 return "unknown";
     424             :         }
     425             : }
     426             : EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
     427             : 
     428             : /**
     429             :  * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
     430             :  * reading offset (0x80, 0x41)
     431             :  * @dev: &drm_device to use
     432             :  * @adapter: I2C-over-aux adapter
     433             :  * @mode: current lspcon mode of operation output variable
     434             :  *
     435             :  * Returns:
     436             :  * 0 on success, sets the current_mode value to appropriate mode
     437             :  * -error on failure
     438             :  */
     439           0 : int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
     440             :                         enum drm_lspcon_mode *mode)
     441             : {
     442             :         u8 data;
     443           0 :         int ret = 0;
     444             :         int retry;
     445             : 
     446           0 :         if (!mode) {
     447           0 :                 drm_err(dev, "NULL input\n");
     448           0 :                 return -EINVAL;
     449             :         }
     450             : 
     451             :         /* Read Status: i2c over aux */
     452           0 :         for (retry = 0; retry < 6; retry++) {
     453           0 :                 if (retry)
     454             :                         usleep_range(500, 1000);
     455             : 
     456           0 :                 ret = drm_dp_dual_mode_read(adapter,
     457             :                                             DP_DUAL_MODE_LSPCON_CURRENT_MODE,
     458             :                                             &data, sizeof(data));
     459           0 :                 if (!ret)
     460             :                         break;
     461             :         }
     462             : 
     463           0 :         if (ret < 0) {
     464           0 :                 drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
     465           0 :                 return -EFAULT;
     466             :         }
     467             : 
     468           0 :         if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
     469           0 :                 *mode = DRM_LSPCON_MODE_PCON;
     470             :         else
     471           0 :                 *mode = DRM_LSPCON_MODE_LS;
     472             :         return 0;
     473             : }
     474             : EXPORT_SYMBOL(drm_lspcon_get_mode);
     475             : 
     476             : /**
     477             :  * drm_lspcon_set_mode: Change LSPCON's mode of operation by
     478             :  * writing offset (0x80, 0x40)
     479             :  * @dev: &drm_device to use
     480             :  * @adapter: I2C-over-aux adapter
     481             :  * @mode: required mode of operation
     482             :  *
     483             :  * Returns:
     484             :  * 0 on success, -error on failure/timeout
     485             :  */
     486           0 : int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
     487             :                         enum drm_lspcon_mode mode)
     488             : {
     489           0 :         u8 data = 0;
     490             :         int ret;
     491           0 :         int time_out = 200;
     492             :         enum drm_lspcon_mode current_mode;
     493             : 
     494           0 :         if (mode == DRM_LSPCON_MODE_PCON)
     495           0 :                 data = DP_DUAL_MODE_LSPCON_MODE_PCON;
     496             : 
     497             :         /* Change mode */
     498           0 :         ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
     499             :                                      &data, sizeof(data));
     500           0 :         if (ret < 0) {
     501           0 :                 drm_err(dev, "LSPCON mode change failed\n");
     502           0 :                 return ret;
     503             :         }
     504             : 
     505             :         /*
     506             :          * Confirm mode change by reading the status bit.
     507             :          * Sometimes, it takes a while to change the mode,
     508             :          * so wait and retry until time out or done.
     509             :          */
     510             :         do {
     511           0 :                 ret = drm_lspcon_get_mode(dev, adapter, &current_mode);
     512           0 :                 if (ret) {
     513           0 :                         drm_err(dev, "can't confirm LSPCON mode change\n");
     514           0 :                         return ret;
     515             :                 } else {
     516           0 :                         if (current_mode != mode) {
     517           0 :                                 msleep(10);
     518           0 :                                 time_out -= 10;
     519             :                         } else {
     520           0 :                                 drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
     521             :                                             mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
     522           0 :                                 return 0;
     523             :                         }
     524             :                 }
     525           0 :         } while (time_out);
     526             : 
     527           0 :         drm_err(dev, "LSPCON mode change timed out\n");
     528           0 :         return -ETIMEDOUT;
     529             : }
     530             : EXPORT_SYMBOL(drm_lspcon_set_mode);

Generated by: LCOV version 1.14