linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sanford Rockowitz <rockowitz-9+fK6rGKj7RBDgjK7y7TUQ@public.gmane.org>
To: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Jean Delvare <jdelvare-l3A5Bk7waGM@public.gmane.org>
Subject: ddc/ci over i2c
Date: Tue, 08 Jul 2014 03:35:39 -0700	[thread overview]
Message-ID: <53BBC97B.5020805@minsoft.com> (raw)

I've been trying to use i2c-dev to read and control monitor settings, 
using the DDC/CI protocol to communicate with address 0x37 on device 
/dev/i2c-n.  Things are sort of working (details below), But it's not 
clear to me to what extent I'm hitting real limits, or if I just don't 
know what I'm doing.  Perhaps I shouldn't even be trying to use i2c-dev 
for this application.  Advice appreciated. And if there's a more 
appropriate place to post this question, I'd appreciate hearing that as 
well.

To review: A monitor is accessed via device /dev/i2c-n, created by the 
video device driver.  EDID data is found by reading address 0x50.  The 
monitor's settings are read and written at address 0x37.

I can communicate with the monitor if either the open-source nouveau or 
radeon drivers are loaded.  Both support i2c_smbus_read_i2c_block_data() 
and i2c_smbus_write_i2c_block_data(), which put the right bytes on the 
wire and handle the ack and nack bits as per the DDC specification. (See 
documentation file i2c/smbus-protocol.)

For the nouveau driver running on Fedora 20, "lsmod | grep i2c" reports:

i2c_piix4              22155  0
i2c_algo_bit           13257  1 nouveau
i2c_dev                14027  0
i2c_core               38656  6 
drm,i2c_dev,i2c_piix4,drm_kms_helper,i2c_algo_bit,nouveau


Here's a simplified example (minimal error checking) of how I'm reading 
the monitor's brightness setting:

    int fh = open("/dev/i2c-0",  O_NONBLOCK|O_RDWR);
    ioctl(fh, I2C_SLAVE, 0x37);

    unsigned char zeroByte = 0x00;
    write(fh, &zeroByte, 1);   // seems to be necessary to reset monitor 
state

    unsigned char ddc_cmd_bytes[] = {
       0x6e,              // address 0x37, shifted left 1 bit
       0x51,              // source address
       0x02 | 0x80,       // number of DDC data bytes, with high bit set
       0x01,              // DDC Get Feature Command
       0x10,              // Feature, Luminosity
       0x00,              // checksum, to be set
    };
    ddc_cmd_bytes[5] = ddc_checksum(ddc_cmd_bytes, 5);    // calculate 
DDC checksum on all bytes
    i2c_smbus_write_i2c_block_data(fh, ddc_cmd_bytes[1], 
sizeof(ddc_cmd_bytes)-2, ddc_cmd_bytes+2);
    // alt: write(fh, ddc_cmd_bytes+1, sizeof(ddc_cmd_bytes)-1); // see 
below
    usleep(5000);

    unsigned char ddc_response_bytes[12];
    unsigned char cmd_byte = 0x00;   // apparently ignored, can be anything
    i2c_smbus_read_i2c_block_data(fh, cmd_byte, 11, readbuf+1);
    // alt read(fh, readbuf+1, 11);   // see below
    ddc_response_bytes[0] = 0x50;     // for proper checksum calculation
    int calculated_checksum = ddc_checksum(readbuf, 11);
    assert(readbuf[11] == calculated_checksum);
    int response_len = ddc_response_bytes[2] & 0x7f;       // always 8 
for DDC Get Value response
    // now parse the response data


When issuing the DDC get feature command (code 0x01), a fixed data block 
of 12 data bytes is returned as shown above (as counted from the 
i2c_cmsbus_read_i2c_block_data() perspective. However, the DDC Get 
Capabilities request (0xf3), can return up to 39 bytes (fixed DDC data 
of 7 bytes plus a "fragment" of up to 32 bytes, depending on the monitor 
being communicated with.).   This is greater than the 32 byte max data 
size supported by i2c_smbus_read_i2c_block_data() (constant 
I2C_SMBUS_I2C_BLOCK_MAX in i2c-dev.h).  And indeed, I've seen 
i2c_smbus_read_i2c_block_data() return truncated responses.

Now things get interesting.

Simply using write() and read() seems to work, when the 
i2c_smbus_..._i2c_block_data() calls in the above code are replaced by 
the commented out write() and read() lines.  So apparently apparently 
write() and read() are handling the algorithm bits (start, top, ack, 
nack) properly.


Now come the key questions:

Am I just getting lucky here, or is the i2c_dev driver (or one of the 
drivers it calls) really doing the right thing for managing the 
algorithm bits for the i2c DDC protocol?

Is there a better way to use lower level services in i2c-dev?

Should I really be writing a device driver?


Finally, the proprietary video drivers.

The proprietary nvidia driver creates the /dev/i2c-n devices.  I can 
read the monitor EDID information on bus address 0x50. and get the 
functionality flags using ioctl I2C_FUNCS.   Functions 
ic2_smbus_read_i2c_block_data() and is2_smbus_write_i2c_block_data() are 
not supported (flags I2C_FUNC_SMBUS_READ_I2C_BLOCK and 
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK are not set).  Attempting to call these 
functions fails with errno=22 (EINVAL - invalid argument) if module 
i2c_algo_bit has not been loaded, and errno=5 (EIO - IO Error) if it has.

Trying to use write() and read() as described above also fails. write() 
appears to succeed, but read() returns invalid data.

Appendix F of the Nvidia driver README discusses i2c support.  It 
appears to be quite dated, referring to the 2.4 and 2.6 kernels. 
According to this document, only the following functionality is 
supported, which would be consistent with what I've seen: I2C_FUNC_I2C, 
I2C_FUNC_SMBUS_QUICK, I2C_FUNC_SMBUS_BYTE, I2C_FUNC_SMBUS_BYTE_DATA, 
I2C_FUNC_SMBUS_WORD_DATA


As for the proprietary fglrx driver, it doesn't even create the 
/dev/i2c-n devices.   End of story.

             reply	other threads:[~2014-07-08 10:35 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-08 10:35 Sanford Rockowitz [this message]
     [not found] ` <53BBC97B.5020805-9+fK6rGKj7RBDgjK7y7TUQ@public.gmane.org>
2014-07-08 22:20   ` ddc/ci over i2c Jean Delvare
     [not found]     ` <20140709002017.4c2b332e-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2014-07-09 16:31       ` Sanford Rockowitz
     [not found]         ` <53BD6E61.8070101-9+fK6rGKj7RBDgjK7y7TUQ@public.gmane.org>
2014-07-28 10:52           ` Jean Delvare
     [not found]             ` <20140728125248.5bbc0f89-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2014-08-09 21:18               ` Sanford Rockowitz
     [not found]                 ` <53E6902B.9000104-9+fK6rGKj7RBDgjK7y7TUQ@public.gmane.org>
2014-08-10  9:05                   ` Jean Delvare
     [not found]                     ` <20140810110528.1b5a825f-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2014-08-10 10:08                       ` Sanford Rockowitz
     [not found]                         ` <53E74487.8020809-9+fK6rGKj7RBDgjK7y7TUQ@public.gmane.org>
2014-08-10 19:13                           ` Jean Delvare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53BBC97B.5020805@minsoft.com \
    --to=rockowitz-9+fk6rgkj7rbdgjk7y7tuq@public.gmane.org \
    --cc=jdelvare-l3A5Bk7waGM@public.gmane.org \
    --cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).