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 128 0.0 %
Date: 2023-03-27 20:00:47 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 :         u8 zero = 0;
      67           0 :         char *tmpbuf = NULL;
      68             :         /*
      69             :          * As sub-addressing is not supported by all adaptors,
      70             :          * always explicitly read from the start and discard
      71             :          * any bytes that come before the requested offset.
      72             :          * This way, no matter whether the adaptor supports it
      73             :          * or not, we'll end up reading the proper data.
      74             :          */
      75           0 :         struct i2c_msg msgs[] = {
      76             :                 {
      77             :                         .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
      78             :                         .flags = 0,
      79             :                         .len = 1,
      80             :                         .buf = &zero,
      81             :                 },
      82             :                 {
      83             :                         .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
      84             :                         .flags = I2C_M_RD,
      85           0 :                         .len = size + offset,
      86             :                         .buf = buffer,
      87             :                 },
      88             :         };
      89             :         int ret;
      90             : 
      91           0 :         if (offset) {
      92           0 :                 tmpbuf = kmalloc(size + offset, GFP_KERNEL);
      93           0 :                 if (!tmpbuf)
      94             :                         return -ENOMEM;
      95             : 
      96           0 :                 msgs[1].buf = tmpbuf;
      97             :         }
      98             : 
      99           0 :         ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
     100           0 :         if (tmpbuf)
     101           0 :                 memcpy(buffer, tmpbuf + offset, size);
     102             : 
     103           0 :         kfree(tmpbuf);
     104             : 
     105           0 :         if (ret < 0)
     106           0 :                 return ret;
     107           0 :         if (ret != ARRAY_SIZE(msgs))
     108             :                 return -EPROTO;
     109             : 
     110           0 :         return 0;
     111             : }
     112             : EXPORT_SYMBOL(drm_dp_dual_mode_read);
     113             : 
     114             : /**
     115             :  * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
     116             :  * @adapter: I2C adapter for the DDC bus
     117             :  * @offset: register offset
     118             :  * @buffer: buffer for write data
     119             :  * @size: sizo of the buffer
     120             :  *
     121             :  * Writes @size bytes to the DP dual mode adaptor registers
     122             :  * starting at @offset.
     123             :  *
     124             :  * Returns:
     125             :  * 0 on success, negative error code on failure
     126             :  */
     127           0 : ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
     128             :                                u8 offset, const void *buffer, size_t size)
     129             : {
     130           0 :         struct i2c_msg msg = {
     131             :                 .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
     132             :                 .flags = 0,
     133           0 :                 .len = 1 + size,
     134             :                 .buf = NULL,
     135             :         };
     136             :         void *data;
     137             :         int ret;
     138             : 
     139           0 :         data = kmalloc(msg.len, GFP_KERNEL);
     140           0 :         if (!data)
     141             :                 return -ENOMEM;
     142             : 
     143           0 :         msg.buf = data;
     144             : 
     145           0 :         memcpy(data, &offset, 1);
     146           0 :         memcpy(data + 1, buffer, size);
     147             : 
     148           0 :         ret = i2c_transfer(adapter, &msg, 1);
     149             : 
     150           0 :         kfree(data);
     151             : 
     152           0 :         if (ret < 0)
     153           0 :                 return ret;
     154           0 :         if (ret != 1)
     155             :                 return -EPROTO;
     156             : 
     157           0 :         return 0;
     158             : }
     159             : EXPORT_SYMBOL(drm_dp_dual_mode_write);
     160             : 
     161             : static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
     162             : {
     163             :         static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
     164             :                 "DP-HDMI ADAPTOR\x04";
     165             : 
     166           0 :         return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
     167             :                       sizeof(dp_dual_mode_hdmi_id)) == 0;
     168             : }
     169             : 
     170             : static bool is_type1_adaptor(uint8_t adaptor_id)
     171             : {
     172           0 :         return adaptor_id == 0 || adaptor_id == 0xff;
     173             : }
     174             : 
     175             : static bool is_type2_adaptor(uint8_t adaptor_id)
     176             : {
     177             :         return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
     178             :                               DP_DUAL_MODE_REV_TYPE2);
     179             : }
     180             : 
     181             : static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
     182             :                               const uint8_t adaptor_id)
     183             : {
     184           0 :         return is_hdmi_adaptor(hdmi_id) &&
     185             :                 (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
     186             :                  DP_DUAL_MODE_TYPE_HAS_DPCD));
     187             : }
     188             : 
     189             : /**
     190             :  * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
     191             :  * @dev: &drm_device to use
     192             :  * @adapter: I2C adapter for the DDC bus
     193             :  *
     194             :  * Attempt to identify the type of the DP dual mode adaptor used.
     195             :  *
     196             :  * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
     197             :  * certain whether we're dealing with a native HDMI port or
     198             :  * a type 1 DVI dual mode adaptor. The driver will have to use
     199             :  * some other hardware/driver specific mechanism to make that
     200             :  * distinction.
     201             :  *
     202             :  * Returns:
     203             :  * The type of the DP dual mode adaptor used
     204             :  */
     205           0 : enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
     206             :                                                    struct i2c_adapter *adapter)
     207             : {
     208           0 :         char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
     209           0 :         uint8_t adaptor_id = 0x00;
     210             :         ssize_t ret;
     211             : 
     212             :         /*
     213             :          * Let's see if the adaptor is there the by reading the
     214             :          * HDMI ID registers.
     215             :          *
     216             :          * Note that type 1 DVI adaptors are not required to implemnt
     217             :          * any registers, and that presents a problem for detection.
     218             :          * If the i2c transfer is nacked, we may or may not be dealing
     219             :          * with a type 1 DVI adaptor. Some other mechanism of detecting
     220             :          * the presence of the adaptor is required. One way would be
     221             :          * to check the state of the CONFIG1 pin, Another method would
     222             :          * simply require the driver to know whether the port is a DP++
     223             :          * port or a native HDMI port. Both of these methods are entirely
     224             :          * hardware/driver specific so we can't deal with them here.
     225             :          */
     226           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
     227             :                                     hdmi_id, sizeof(hdmi_id));
     228           0 :         drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
     229             :                     ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
     230           0 :         if (ret)
     231             :                 return DRM_DP_DUAL_MODE_UNKNOWN;
     232             : 
     233           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
     234             :                                     &adaptor_id, sizeof(adaptor_id));
     235           0 :         drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
     236           0 :         if (ret == 0) {
     237           0 :                 if (is_lspcon_adaptor(hdmi_id, adaptor_id))
     238             :                         return DRM_DP_DUAL_MODE_LSPCON;
     239           0 :                 if (is_type2_adaptor(adaptor_id)) {
     240           0 :                         if (is_hdmi_adaptor(hdmi_id))
     241             :                                 return DRM_DP_DUAL_MODE_TYPE2_HDMI;
     242             :                         else
     243           0 :                                 return DRM_DP_DUAL_MODE_TYPE2_DVI;
     244             :                 }
     245             :                 /*
     246             :                  * If not a proper type 1 ID, still assume type 1, but let
     247             :                  * the user know that we may have misdetected the type.
     248             :                  */
     249           0 :                 if (!is_type1_adaptor(adaptor_id))
     250           0 :                         drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
     251             : 
     252             :         }
     253             : 
     254           0 :         if (is_hdmi_adaptor(hdmi_id))
     255             :                 return DRM_DP_DUAL_MODE_TYPE1_HDMI;
     256             :         else
     257           0 :                 return DRM_DP_DUAL_MODE_TYPE1_DVI;
     258             : }
     259             : EXPORT_SYMBOL(drm_dp_dual_mode_detect);
     260             : 
     261             : /**
     262             :  * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
     263             :  * @dev: &drm_device to use
     264             :  * @type: DP dual mode adaptor type
     265             :  * @adapter: I2C adapter for the DDC bus
     266             :  *
     267             :  * Determine the max TMDS clock the adaptor supports based on the
     268             :  * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
     269             :  * register (on type2 adaptors). As some type 1 adaptors have
     270             :  * problems with registers (see comments in drm_dp_dual_mode_detect())
     271             :  * we don't read the register on those, instead we simply assume
     272             :  * a 165 MHz limit based on the specification.
     273             :  *
     274             :  * Returns:
     275             :  * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
     276             :  */
     277           0 : int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
     278             :                                     struct i2c_adapter *adapter)
     279             : {
     280             :         uint8_t max_tmds_clock;
     281             :         ssize_t ret;
     282             : 
     283             :         /* native HDMI so no limit */
     284           0 :         if (type == DRM_DP_DUAL_MODE_NONE)
     285             :                 return 0;
     286             : 
     287             :         /*
     288             :          * Type 1 adaptors are limited to 165MHz
     289             :          * Type 2 adaptors can tells us their limit
     290             :          */
     291           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
     292             :                 return 165000;
     293             : 
     294           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
     295             :                                     &max_tmds_clock, sizeof(max_tmds_clock));
     296           0 :         if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
     297           0 :                 drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
     298           0 :                 return 165000;
     299             :         }
     300             : 
     301           0 :         return max_tmds_clock * 5000 / 2;
     302             : }
     303             : EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
     304             : 
     305             : /**
     306             :  * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
     307             :  * @dev: &drm_device to use
     308             :  * @type: DP dual mode adaptor type
     309             :  * @adapter: I2C adapter for the DDC bus
     310             :  * @enabled: current state of the TMDS output buffers
     311             :  *
     312             :  * Get the state of the TMDS output buffers in the adaptor. For
     313             :  * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
     314             :  * register. As some type 1 adaptors have problems with registers
     315             :  * (see comments in drm_dp_dual_mode_detect()) we don't read the
     316             :  * register on those, instead we simply assume that the buffers
     317             :  * are always enabled.
     318             :  *
     319             :  * Returns:
     320             :  * 0 on success, negative error code on failure
     321             :  */
     322           0 : int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
     323             :                                      enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
     324             :                                      bool *enabled)
     325             : {
     326             :         uint8_t tmds_oen;
     327             :         ssize_t ret;
     328             : 
     329           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
     330           0 :                 *enabled = true;
     331           0 :                 return 0;
     332             :         }
     333             : 
     334           0 :         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
     335             :                                     &tmds_oen, sizeof(tmds_oen));
     336           0 :         if (ret) {
     337           0 :                 drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
     338           0 :                 return ret;
     339             :         }
     340             : 
     341           0 :         *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
     342             : 
     343           0 :         return 0;
     344             : }
     345             : EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
     346             : 
     347             : /**
     348             :  * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
     349             :  * @dev: &drm_device to use
     350             :  * @type: DP dual mode adaptor type
     351             :  * @adapter: I2C adapter for the DDC bus
     352             :  * @enable: enable (as opposed to disable) the TMDS output buffers
     353             :  *
     354             :  * Set the state of the TMDS output buffers in the adaptor. For
     355             :  * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register.
     356             :  * Type1 adaptors do not support any register writes.
     357             :  *
     358             :  * Returns:
     359             :  * 0 on success, negative error code on failure
     360             :  */
     361           0 : int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
     362             :                                      struct i2c_adapter *adapter, bool enable)
     363             : {
     364           0 :         uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
     365             :         ssize_t ret;
     366             :         int retry;
     367             : 
     368           0 :         if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
     369             :                 return 0;
     370             : 
     371             :         /*
     372             :          * LSPCON adapters in low-power state may ignore the first write, so
     373             :          * read back and verify the written value a few times.
     374             :          */
     375           0 :         for (retry = 0; retry < 3; retry++) {
     376             :                 uint8_t tmp;
     377             : 
     378           0 :                 ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
     379             :                                              &tmds_oen, sizeof(tmds_oen));
     380           0 :                 if (ret) {
     381           0 :                         drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
     382             :                                     enable ? "enable" : "disable", retry + 1);
     383           0 :                         return ret;
     384             :                 }
     385             : 
     386           0 :                 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
     387             :                                             &tmp, sizeof(tmp));
     388           0 :                 if (ret) {
     389           0 :                         drm_dbg_kms(dev,
     390             :                                     "I2C read failed during TMDS output buffer %s (%d attempts)\n",
     391             :                                     enable ? "enabling" : "disabling", retry + 1);
     392           0 :                         return ret;
     393             :                 }
     394             : 
     395           0 :                 if (tmp == tmds_oen)
     396             :                         return 0;
     397             :         }
     398             : 
     399           0 :         drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
     400             :                     enable ? "enabling" : "disabling");
     401             : 
     402           0 :         return -EIO;
     403             : }
     404             : EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
     405             : 
     406             : /**
     407             :  * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
     408             :  * @type: DP dual mode adaptor type
     409             :  *
     410             :  * Returns:
     411             :  * String representation of the DP dual mode adaptor type
     412             :  */
     413           0 : const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
     414             : {
     415           0 :         switch (type) {
     416             :         case DRM_DP_DUAL_MODE_NONE:
     417             :                 return "none";
     418             :         case DRM_DP_DUAL_MODE_TYPE1_DVI:
     419           0 :                 return "type 1 DVI";
     420             :         case DRM_DP_DUAL_MODE_TYPE1_HDMI:
     421           0 :                 return "type 1 HDMI";
     422             :         case DRM_DP_DUAL_MODE_TYPE2_DVI:
     423           0 :                 return "type 2 DVI";
     424             :         case DRM_DP_DUAL_MODE_TYPE2_HDMI:
     425           0 :                 return "type 2 HDMI";
     426             :         case DRM_DP_DUAL_MODE_LSPCON:
     427           0 :                 return "lspcon";
     428             :         default:
     429           0 :                 WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
     430             :                 return "unknown";
     431             :         }
     432             : }
     433             : EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
     434             : 
     435             : /**
     436             :  * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
     437             :  * reading offset (0x80, 0x41)
     438             :  * @dev: &drm_device to use
     439             :  * @adapter: I2C-over-aux adapter
     440             :  * @mode: current lspcon mode of operation output variable
     441             :  *
     442             :  * Returns:
     443             :  * 0 on success, sets the current_mode value to appropriate mode
     444             :  * -error on failure
     445             :  */
     446           0 : int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
     447             :                         enum drm_lspcon_mode *mode)
     448             : {
     449             :         u8 data;
     450           0 :         int ret = 0;
     451             :         int retry;
     452             : 
     453           0 :         if (!mode) {
     454           0 :                 drm_err(dev, "NULL input\n");
     455           0 :                 return -EINVAL;
     456             :         }
     457             : 
     458             :         /* Read Status: i2c over aux */
     459           0 :         for (retry = 0; retry < 6; retry++) {
     460           0 :                 if (retry)
     461             :                         usleep_range(500, 1000);
     462             : 
     463           0 :                 ret = drm_dp_dual_mode_read(adapter,
     464             :                                             DP_DUAL_MODE_LSPCON_CURRENT_MODE,
     465             :                                             &data, sizeof(data));
     466           0 :                 if (!ret)
     467             :                         break;
     468             :         }
     469             : 
     470           0 :         if (ret < 0) {
     471           0 :                 drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
     472           0 :                 return -EFAULT;
     473             :         }
     474             : 
     475           0 :         if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
     476           0 :                 *mode = DRM_LSPCON_MODE_PCON;
     477             :         else
     478           0 :                 *mode = DRM_LSPCON_MODE_LS;
     479             :         return 0;
     480             : }
     481             : EXPORT_SYMBOL(drm_lspcon_get_mode);
     482             : 
     483             : /**
     484             :  * drm_lspcon_set_mode: Change LSPCON's mode of operation by
     485             :  * writing offset (0x80, 0x40)
     486             :  * @dev: &drm_device to use
     487             :  * @adapter: I2C-over-aux adapter
     488             :  * @mode: required mode of operation
     489             :  *
     490             :  * Returns:
     491             :  * 0 on success, -error on failure/timeout
     492             :  */
     493           0 : int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
     494             :                         enum drm_lspcon_mode mode)
     495             : {
     496           0 :         u8 data = 0;
     497             :         int ret;
     498           0 :         int time_out = 200;
     499             :         enum drm_lspcon_mode current_mode;
     500             : 
     501           0 :         if (mode == DRM_LSPCON_MODE_PCON)
     502           0 :                 data = DP_DUAL_MODE_LSPCON_MODE_PCON;
     503             : 
     504             :         /* Change mode */
     505           0 :         ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
     506             :                                      &data, sizeof(data));
     507           0 :         if (ret < 0) {
     508           0 :                 drm_err(dev, "LSPCON mode change failed\n");
     509           0 :                 return ret;
     510             :         }
     511             : 
     512             :         /*
     513             :          * Confirm mode change by reading the status bit.
     514             :          * Sometimes, it takes a while to change the mode,
     515             :          * so wait and retry until time out or done.
     516             :          */
     517             :         do {
     518           0 :                 ret = drm_lspcon_get_mode(dev, adapter, &current_mode);
     519           0 :                 if (ret) {
     520           0 :                         drm_err(dev, "can't confirm LSPCON mode change\n");
     521           0 :                         return ret;
     522             :                 } else {
     523           0 :                         if (current_mode != mode) {
     524           0 :                                 msleep(10);
     525           0 :                                 time_out -= 10;
     526             :                         } else {
     527           0 :                                 drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
     528             :                                             mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
     529           0 :                                 return 0;
     530             :                         }
     531             :                 }
     532           0 :         } while (time_out);
     533             : 
     534           0 :         drm_err(dev, "LSPCON mode change timed out\n");
     535           0 :         return -ETIMEDOUT;
     536             : }
     537             : EXPORT_SYMBOL(drm_lspcon_set_mode);

Generated by: LCOV version 1.14