rockowitz / ddcutil

Control monitor settings using DDC/CI and USB
http://www.ddcutil.com
GNU General Public License v2.0
972 stars 40 forks source link

Picture-by-Picture #163

Open pjaitken opened 3 years ago

pjaitken commented 3 years ago

I'm looking for a way to change the Picture-by-Picture (PBP) input on a Philips 499P9. The PBP mode allows it to display two inputs side-by-side.

ddcutil reports the monitor as two displays on different I2C buses. When reading, the same values seem to be returned from both. Writing to either display or bus affects the monitor as a whole.

Writing to vcp 60 only changes the primary input. However when the inputs are changed manually, reading vcp 60 returns the value for the most-recently changed input - whether the primary or PBP.

When running from a single input, vcp e6 reports sl=0x01; for PBP it reports 0x00. Writing has no effect. There are a handful of other manufacturer vcp's, but they don't change when the inputs are changed and writing to them doesn't have any obvious effect.

"ddcutil watch" doesn't help: although vcp 2 reports "One or more new control values have been saved", vcp 52 always reports value 0x02 - even after writing 1 to vcp 2, which then reports "No new control values".

Thanks.

bugrasan commented 3 years ago

Looking for the same solution for the same monitor. I was hoping that one of the E? codes work but i can't read their values even some of them shown in the capabilities:

   Feature: E9 (manufacturer specific feature)
      Values: 00 02 (interpretation unavailable)
   Feature: EB (manufacturer specific feature)
      Values: 00 01 02 03 (interpretation unavailable)
   Feature: F0 (manufacturer specific feature)
      Values: 00 01 (interpretation unavailable)
   Feature: FD (manufacturer specific feature)
   Feature: FF (manufacturer specific feature)

and E6 doesn't show any the difference in PBP mode (on or off):

$ ddcutil --bus=x getvcp e6
VCP code 0xe6 (Manufacturer Specific         ): mh=0x00, ml=0x00, sh=0x00, sl=0x00

in addition to PBP i would be also interested to switch the USB KVM (Auto, USB-C, USB-Up).

interestingly when using ddcutil --bus=x watch i can't use the on-screen-display, the timeout seems to get borked, and it disappears immediately. also the VCP code 0x02 repeats until ddcutil "loop guard" kicks in.

pjaitken commented 3 years ago

For me, the low bit of VCP e6 does indicate the PBP mode:

With PBP mode enabled (ie, two inputs / split screen):

$ ddcutil -b 1 getvcp e6
VCP code 0xe6 (Manufacturer Specific         ): mh=0x00, ml=0x00, sh=0x00, sl=0x00

Without PBP mode (ie, one input):

$ ddcutil -b 1 getvcp e6
VCP code 0xe6 (Manufacturer Specific         ): mh=0x00, ml=0x00, sh=0x00, sl=0x01

"scan" and "capabilities" and report different sets of manufacturer specific codes:

$ ddcutil -b 1 getvcp scan | grep -i manufacturer
VCP code 0xe0 (Manufacturer Specific         ): mh=0x00, ml=0x14, sh=0x00, sl=0x03
VCP code 0xe6 (Manufacturer Specific         ): mh=0x00, ml=0x00, sh=0x00, sl=0x00
VCP code 0xeb (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
VCP code 0xf0 (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
VCP code 0xfe (Manufacturer Specific         ): Maximum retries exceeded

$ ddcutil -b 1 capabilities | grep -i manufacturer
   Feature: E9 (manufacturer specific feature)
   Feature: EB (manufacturer specific feature)
   Feature: F0 (manufacturer specific feature)
   Feature: FD (manufacturer specific feature)
   Feature: FF (manufacturer specific feature)

Reading manufacturer specific codes:

$ ddcutil -b 1 getvcp e0
VCP code 0xe0 (Manufacturer Specific         ): mh=0x00, ml=0x14, sh=0x00, sl=0x03
$ ddcutil -b 1 getvcp e6
VCP code 0xe6 (Manufacturer Specific         ): mh=0x00, ml=0x00, sh=0x00, sl=0x00
$ ddcutil -b 1 getvcp eb
VCP code 0xeb (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
$ ddcutil -b 1 getvcp f0
VCP code 0xf0 (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
$ ddcutil -b 1 getvcp fe
VCP code 0xfe (Manufacturer Specific         ): Maximum retries exceeded

$ ddcutil -b 1 getvcp e9
VCP code 0xe9 (Manufacturer Specific         ): Unsupported feature code
$ ddcutil -b 1 getvcp eb 
VCP code 0xeb (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
$ ddcutil -b 1 getvcp f0
VCP code 0xf0 (Manufacturer Specific         ): mh=0x00, ml=0x01, sh=0x00, sl=0x01
$ ddcutil -b 1 getvcp fd
VCP code 0xfd (Manufacturer Specific         ): Unsupported feature code
$ ddcutil -b 1 getvcp ff
VCP code 0xff (Manufacturer Specific         ): Unsupported feature code

Writing manufacturer specific codes:

vcp e0 can be set to 0 or 3 with no obvious effect.
vcp e6 can be written, but never changes from 0.
vcp eb can be set 0 - 3 with no obvious effect.
vcp f0 can be set 0 - ff; the upper bits are lost, only the bottom bit is kept (0 - 1).
rockowitz commented 3 years ago

@pjaitken I have, as they say, been admiring your problem, and trying to think of useful things to suggest. PBP is so beyond what was considered when MCCS was defined. You've looked in all the obvious places, and then some. Here are some observations and questions (many of which are undoubtedly obvious).

Regards, Sanford

rockowitz commented 3 years ago

@bugrasan Most of my reply to @pjaitken is for you as well.

The one thing I would add is that the watch command is problematic. The version in branch 1.0.0-dev better handles error conditions. In MCCS, feature x52 became a FIFO, meant to be read repeatedly until a value of x00 is returned. Branch 1.0.0-dev adds command line option --x52-no-fifo, which forces watch** to always use MCCS 2.0/2.1 behaviour, reading a single feature id from feature x52 and then checking feature x02 to see if there are more changed features.

I'd appreciate it if you run ddcutil interrogate in the various monitor states and send the output, of course as attachments of some sort.

Regards, Sanford

pjaitken commented 3 years ago

Thanks @rockowitz. I've emailed you some "ddcutil interrogate" outputs.

The hardware configuration is debian connected to the displayPort + HDMI-1 inputs, and macOS connected to HDMI-2. No windows machines. I couldn't build ddcutil for macOS (no libudev), so no outputs from there.

pjaitken commented 3 years ago

FWIW, ddcutil getvcp SCAN gives different output for some VCPs compared with discrete queries:

SCAN:

VCP code 0x20 (Horizontal Position (Phase)   ): current value =     0, max value =     1
VCP code 0x30 (Vertical Position (Phase)     ): current value =     0, max value =     1
VCP code 0x52 (Active control                ): Value: 0x00

Discrete getvcp queries:

VCP code 0x20 (Horizontal Position (Phase)   ): current value =   514, max value = 65535
VCP code 0x30 (Vertical Position (Phase)     ): current value =   514, max value = 65535
VCP code 0x52 (Active control                ): Value: 0x02

Also, --x52-no-fifo doesn't make any difference.

rockowitz commented 3 years ago

I'm not sure what to make of the feature x20 and x30 discrepancies. These features are in the Geometry section of the MCCS spec, which contains features like pincushion that apply only to CRTs. What these features mean for flat panels is unclear. A pixel offset?

And yet, 2 of the 3 monitors in front of me list these features in their capabilities string. All 3 reply to getvcp requests for the features. And all 3 show show a similar deviation in the output of getvcp 20/getvcp 30 versus getvcp scan.

The obvious suspect is some buffer corruption in ddcutil. But tracing at the lowest I2C level shows that the request packets are identical, while the response packets diverge.

I've improved the tracing in branch 1.0.0-dev to make this easier to see. Use the command

$ ddcutil getvcp 20 --verbose --trcfunc get_raw_value_for_feature_metadata, --trcfunc i2c_fileio_reader, --trcfunc_i2c_fileio_writer

and similar commands for feature x30 and scan

What's of interest are the i2c_fileio_writer() and i2c_fileio_reader() calls within get_raw_value_for_feature_metadata(). i2c_fileio_writer() writes the same request packets for feature x20/x30 for scan versus for discrete getvcp requests.. But the response packets are different. The ending trace for get_raw_value_for_feature_metadata() is properly interpreting the returned bytes. (Note: Only the first 11 bytes in the buffer reported by i2c_fileio_reader() are the actual response. The 11th byte is a checksum.)

rockowitz commented 3 years ago

Re the x52 deviations, I would expect the value of feature x52 to vary based on the monitor's state. That the value of x52 is x00 for scan suggests that the read of feature x02 earlier in the scan returned either x01 (No new control values) or xff (No user controls are present).

pjaitken commented 3 years ago

I see the same: different data returned by "scan" versus "getvcp".

Here's the output of ddcutil getvcp SCAN --verbose --trcfunc=get_raw_value_for_feature_metadata --trcfunc=i2c_fileio_reader --trcfunc=i2c_fileio_writer:

Getting data for non-table VCP code 0x20 - Horizontal Position (Phase):
(get_raw_value_for_feature_metadata) Starting. frec=0x55cf03763840, feature_code=0x20
(i2c_fileio_writer             ) Starting. fh=3, filename=/dev/i2c-1, slave_address=0x37, bytect=5, pbytes=0x55cf0375e721 -> 51 82 01 20 9c
(i2c_fileio_writer             ) Done. Returning: OK(0): success
(i2c_fileio_reader             ) Starting. fd=3, fn=/dev/i2c-1, bytect=20, slave_address=0x37, single_byte_reads=false
(i2c_fileio_reader             ) Returning: OK(0): success, readbuf: 6e 88 02 00 20 01 00 01 00 00 94 00 00 00 00 00 00 00 00 00
(get_raw_value_for_feature_metadata) Done.     Returning NULL, *pvalrec ->  
         Single_Vcp_Value at 0x55cf0375e920:
            Opcode:          0x20
            Value type:      DDCA_NON_TABLE_VCP_VALUE (0x01)
            max_val:     1 - 0x0001
            cur_val:     0 - 0x0000
            mh:          0x00
            ml:          0x01
            sh:          0x00
            sl:          0x00
Raw value: opcode=0x20, mh=0x00, ml=0x01, sh=0x00, sl=0x00, max_val=1 (0x0001), cur_val=0 (0x0000)
VCP code 0x20 (Horizontal Position (Phase)   ): current value =     0, max value =     1

Getting data for non-table VCP code 0x30 - Vertical Position (Phase):
(get_raw_value_for_feature_metadata) Starting. frec=0x55cf037643a0, feature_code=0x30
(i2c_fileio_writer             ) Starting. fh=3, filename=/dev/i2c-1, slave_address=0x37, bytect=5, pbytes=0x55cf0375e011 -> 51 82 01 30 8c
(i2c_fileio_writer             ) Done. Returning: OK(0): success
(i2c_fileio_reader             ) Starting. fd=3, fn=/dev/i2c-1, bytect=20, slave_address=0x37, single_byte_reads=false
(i2c_fileio_reader             ) Returning: OK(0): success, readbuf: 6e 88 02 00 30 01 00 01 00 00 84 00 00 00 00 00 00 00 00 00
(get_raw_value_for_feature_metadata) Done.     Returning NULL, *pvalrec ->  
         Single_Vcp_Value at 0x55cf0375e920:
            Opcode:          0x30
            Value type:      DDCA_NON_TABLE_VCP_VALUE (0x01)
            max_val:     1 - 0x0001
            cur_val:     0 - 0x0000
            mh:          0x00
            ml:          0x01
            sh:          0x00
            sl:          0x00
Raw value: opcode=0x30, mh=0x00, ml=0x01, sh=0x00, sl=0x00, max_val=1 (0x0001), cur_val=0 (0x0000)
VCP code 0x30 (Vertical Position (Phase)     ): current value =     0, max value =     1

Here are the outputs of ddcutil getvcp 20 --verbose --trcfunc=get_raw_value_for_feature_metadata --trcfunc=i2c_fileio_reader --trcfunc=i2c_fileio_writer and ddcutil getvcp 20 --verbose --trcfunc=get_raw_value_for_feature_metadata --trcfunc=i2c_fileio_reader --trcfunc=i2c_fileio_writer:

Getting data for non-table VCP code 0x20 - Horizontal Position (Phase):
(get_raw_value_for_feature_metadata) Starting. frec=0x558151a73110, feature_code=0x20
(i2c_fileio_writer             ) Starting. fh=3, filename=/dev/i2c-1, slave_address=0x37, bytect=5, pbytes=0x558151a72a71 -> 51 82 01 20 9c
(i2c_fileio_writer             ) Done. Returning: OK(0): success
(i2c_fileio_reader             ) Starting. fd=3, fn=/dev/i2c-1, bytect=20, slave_address=0x37, single_byte_reads=false
(i2c_fileio_reader             ) Returning: OK(0): success, readbuf: 6e 88 02 00 20 00 ff ff 02 02 94 00 00 00 00 00 00 00 00 00
(get_raw_value_for_feature_metadata) Done.     Returning NULL, *pvalrec -> 
         Single_Vcp_Value at 0x558151a72ae0:
            Opcode:          0x20
            Value type:      DDCA_NON_TABLE_VCP_VALUE (0x01)
            max_val:     65535 - 0xffff
            cur_val:     514 - 0x0202
            mh:          0xff
            ml:          0xff
            sh:          0x02
            sl:          0x02
Raw value: opcode=0x20, mh=0xff, ml=0xff, sh=0x02, sl=0x02, max_val=65535 (0xffff), cur_val=514 (0x0202)
VCP code 0x20 (Horizontal Position (Phase)   ): current value =   514, max value = 65535

Getting data for non-table VCP code 0x30 - Vertical Position (Phase):
(get_raw_value_for_feature_metadata) Starting. frec=0x556b2c0e9110, feature_code=0x30
(i2c_fileio_writer             ) Starting. fh=3, filename=/dev/i2c-1, slave_address=0x37, bytect=5, pbytes=0x556b2c0e8a71 -> 51 82 01 30 8c
(i2c_fileio_writer             ) Done. Returning: OK(0): success
(i2c_fileio_reader             ) Starting. fd=3, fn=/dev/i2c-1, bytect=20, slave_address=0x37, single_byte_reads=false
(i2c_fileio_reader             ) Returning: OK(0): success, readbuf: 6e 88 02 00 30 00 ff ff 02 02 84 00 00 00 00 00 00 00 00 00
(get_raw_value_for_feature_metadata) Done.     Returning NULL, *pvalrec -> 
         Single_Vcp_Value at 0x556b2c0e8ae0:
            Opcode:          0x30
            Value type:      DDCA_NON_TABLE_VCP_VALUE (0x01)
            max_val:     65535 - 0xffff
            cur_val:     514 - 0x0202
            mh:          0xff
            ml:          0xff
            sh:          0x02
            sl:          0x02
Raw value: opcode=0x30, mh=0xff, ml=0xff, sh=0x02, sl=0x02, max_val=65535 (0xffff), cur_val=514 (0x0202)
VCP code 0x30 (Vertical Position (Phase)     ): current value =   514, max value = 65535
pjaitken commented 3 years ago

The earlier read of 0x02 returned 2. Writing 1 to 0x02 resets 0x02 - but 0x52 continues to report 0x02:

$DDC getvcp -b 1 x02
VCP code 0x02 (New control value             ): One or more new control values have been saved (0x02)

$DDC getvcp -b 1 x52
VCP code 0x52 (Active control                ): Value: 0x02

$DDC setvcp -b 1 x02 1

$DDC getvcp -b 1 x02
VCP code 0x02 (New control value             ): No new control values (0x01)

$DDC getvcp -b 1 x52
VCP code 0x52 (Active control                ): Value: 0x02
rockowitz commented 3 years ago

Hi Paul,

@pjaitken, If you're still trying to figure out the DDC command sequences and the Phillips control software runs on your Mac, I may have a solution. Recently I came across an inexpensive device called I2CDriver that can sniff I2C traffic. I also got lucky and found a DVI DDC breakout cable online on closeout for $22.50 plus shipping, which saved me from having to make the cable myself. I had to fiddle a bit to get from the connector on the cable, which is the header for a 10 pin ribbon cable, but I fished an old COM port ribbon cable out of the parts bin.

I had to fiddle a bit to get things working - this is a tool for people who can figure things out on their own. I can now capture the I2C traffic, including ACKs and NACKs, in a CSV file. The next step will be to write a Python program that interprets the sequences of "READ, 8, ACK, WRITE 2" etc. as DDC commands.

A couple things are worth pointing out. There are 4 leads from the I2CDriver device: SDA, SCL, GROUND, and 5v VCC. The last is irrelevant for sniffing the line and should be left disconnected. It exists to power a small device being driven from I2CDriver.

Second, I2CDriver has 3 modes: command for controlling the attached device, monitor for watching bits go by on the line, and capture for collecting the traffic and writing it to a file. When in capture mode it doesn't look like anything is happening. Only when capture mode is terminated is output written to the log file.

Regards, Sanford

pjaitken commented 3 years ago

@bugrasan I found how to set/swap the PBP inputs:

setvcp 0x60 controls the input source:

           0f: DisplayPort-1
           10: DisplayPort-2
           11: HDMI-1
           12: HDMI-2

PBP mode has to be set manually through the menus. However, once in PBP mode, setvcp --noverify 0xF6 1 swaps the 0x60 and PBP displays.

So to show $RIGHT and $LEFT outputs in PBP mode:

    set 0x60 $RIGHT          # appears on the left
    set --noverify 0xF6 1    # swaps left and right
    set 0x60 $LEFT           # appears on the left

Other VCPs I discovered:

E0: R/W Audio source: 1=HDMI-1, 3=DisplayPort-1 Presumably 2=HDMI-2 and 4=Display-2.
E6: Read: 0x00 indicates PBP mode; 0x01 indicates single input mode. Not writeable.
EB: R/W smartresponse: 0=off, 1=fast, 2=faster, 3=fastest.
F0: R/W smartcontrast: 0=off, 1=on.
F6: Write --noverify: 1=swap 0x60 and PBP (no effect in non-PBP mode).

I did not find any VCPs relating to USB settings.

hadess commented 1 year ago

I'm looking for a way to change the Picture-by-Picture (PBP) input on a Philips 499P9. The PBP mode allows it to display two inputs side-by-side.

I have a Philips 346P1CRH and tested the SmartControl tool that Philips offers (version 6.9.00 after it auto-updated) and PBP wasn't available for selection even though I had a laptop and my desktop machine attached to it.

Capture2

Does the Philips tool work for you to configure PBP? I had the monitor connected through USB as well as HDMI, to no avail. This seems to confirm that the lack of support is a problem with the monitor not offering the functionality through DDC/CI, rather than us not finding out how to enable it. I was personally interested in changing the aspect ratio to 16:9, but that's not available either.

Device and software info screenshot

pjaitken commented 1 year ago

I was able to set PBP like this:

    set 0x60 $RIGHT          # appears on the left
    set --noverify 0xF6 1    # swaps left and right
    set 0x60 $LEFT           # appears on the left

The values for 0x60 are:

setvcp 0x60 controls the input source:

           0f: DisplayPort-1
           10: DisplayPort-2
           11: HDMI-1
           12: HDMI-2
hadess commented 1 year ago

I was able to set PBP like this:

So, not through the Philips tool, and you still need to enable PBP manually, right?

pjaitken commented 1 year ago

I found that PBP mode had to be set manually through the menus.

I'm not able to test with the Philips tool since I nolonger have the monitor.

Be careful when writing to unknown VCPs.